Những bước và biện pháp nào tôi có thể thực hiện để ngăn chặn các vết lõm sâu trong mã của mình?
Những bước và biện pháp nào tôi có thể thực hiện để ngăn chặn các vết lõm sâu trong mã của mình?
Câu trả lời:
Sự thụt sâu thường không phải là vấn đề nếu mọi chức năng / phương thức trong chương trình của bạn thực hiện một và chỉ một điều. Thỉnh thoảng, có thể cần phải lồng các điều kiện sâu một vài cấp độ, nhưng tôi có thể thành thật nói rằng tôi chỉ viết mã thụt sâu một vài lần trong hơn 12 năm mã hóa.
Điều tốt nhất bạn có thể làm là trích xuất các phương thức:
int Step1(int state)
{
if (state == 100)
{
return Step2(state);
}
else
{
return Step3(state);
}
}
int Step2(int state)
{
if (state != 100)
{
throw new InvalidStateException(2, state);
}
// ....
}
if
điều kiện phức tạp . Thực hiện đến cùng cực, bạn sẽ kết thúc với mã giả thực thi.
else
các khối không cần thiết .
Có lẽ bạn có thể xem xét các điều khoản bảo vệ ?
thay vì
public void DoSomething(int value){
if (someCondition){
if(someOtherCondition){
if(yetAnotherCondition){
//Finally execute some code
}
}
}
}
Làm
public void DoSomething(int value){
if(!(someCondition && someOtherCondition && yetAnotherCondition)){
return;
//Maybe throw exception if all preconditions must be true
}
//All preconditions are safe execute code
}
Nếu bạn có cơ hội, tôi sẽ giới thiệu bạn đọc Code Complete của Steve McConnell. Anh ấy có rất nhiều lời khuyên tuyệt vời về những chủ đề này.
Để biết thêm về "mệnh đề bảo vệ", hãy xem: https://sourcemaking.com/refactoring/replace-nested-conditable-with-guard-clauses
Đảo ngược if
s của bạn .
Thay vì:
if (foo != null)
{
something;
something;
if (x)
{
something;
}
something;
}
else
{
boohoo;
}
Tôi sẽ viết:
if (foo == null)
{
boohoo;
return;
}
something;
something;
if (x)
{
something;
}
something;
Áp dụng tương tự cho if
- else
khối. Nếu else
ngắn hơn / ít lồng nhau, sau đó hoàn nguyên chúng.
Kiểm tra giá trị của các tham số ở một nơi
Kiểm tra tất cả các tham số cho các giá trị bất hợp pháp ngay khi bạn nhập phương thức của mình, sau đó tiến hành biết rằng bạn an toàn. Nó làm cho mã dễ đọc hơn, nhưng nó cũng giúp bạn tiết kiệm được các khối có điều kiện sau này và trải rộng các kiểm tra này trên chương trình con.
If
s ở đầu mã dừng dòng thực thi do một số điều kiện không được đáp ứng còn được gọi là mệnh đề bảo vệ , như @JasonTuran chỉ ra. Và điều đó dường như gần như có được một cái tên khác biệt.
Thông thường, tôi đã thấy rằng mã thụt sâu thường là mã có vấn đề. Nếu bạn đang đối mặt với vấn đề này, thì hãy lùi lại và đánh giá xem chức năng của bạn có đang làm quá nhiều việc không.
Đồng thời, để trả lời câu hỏi của bạn, nếu có nhu cầu thụt sâu đến mức đó, tôi sẽ đề nghị bạn để nó ở đó. Vì lý do đơn giản là trong mã như vậy, việc thụt lề sẽ giúp ích vì nó có khả năng là một đoạn mã rất dài.
Chia các thành phần lồng nhau (đặc biệt là các thành phần lặp đi lặp lại) thành các chức năng riêng biệt (điều này dễ dàng hơn nếu ngôn ngữ của bạn hỗ trợ các lần đóng) hoặc thay thế một loạt các vòng lặp lồng nhau bằng một đệ quy.
Ngoài ra, thụt hai không gian thay vì bốn.
Tôi không thấy các vết lõm sâu như một vấn đề phân loại cần được loại bỏ (tôi cũng không thấy tái cấu trúc là câu trả lời thực sự cho mọi thứ).
Thông thường thay vì ifs lồng nhau, tôi thích viết các câu logic:
if (foo && bar && baz)
thay vì
if foo
if bar
if baz
Tôi đã không tin vào bản thân mình, nhưng theo Code Complete thì đây là một nơi thích hợp để sử dụng break
(nếu nhóm của bạn ở trên tàu). Tôi tưởng tượng điều này dễ chấp nhận hơn với các lập trình viên C ++, nơi break
được sử dụng trong các switch
câu lệnh so với các lập trình viên Delphi, nơi break
chỉ được sử dụng khi bạn không cảm thấy muốn viết một while
vòng lặp.
Ấn thực sự là một suy nghĩ để chiến đấu, thực sự. Điều tôi học được là chia phương thức thành các phần trước, sau đó sử dụng một mẹo kỳ lạ để bỏ qua mọi phần sau nếu một phần không thành công. Đây là một ví dụ :
Thay vì :
{if (networkCardIsOn() == true)
{if (PingToServer() == true)
{if (AccesLogin(login,pass) == true)
{if (nextCondition == true)
...
}
}
}
Tôi hiện đang viết:
{vbContinue = true;
if (vbContinue) {
vbContinue = networkCardIsOn();
if (vbContinue == false) {
code to Handle This Error();
}
}
if (vbContinue) {
vbContinue = PingToServer();
if (vbContinue == false) {
code to HandleThisError2();
}
}
if (vbContinue) {
vbContinue = AccesLogin(login,pass);
if (vbContinue == false) {
HandleThisErrorToo();
}
}
...
Điều này có vẻ lạ đối với tôi lúc đầu, nhưng vì tôi sử dụng nó, chi phí bảo trì đã được chia cho một nửa và bộ não của tôi mát hơn vào cuối ngày.
Trong thực tế, lợi ích được giới thiệu bởi "kỹ thuật" này là độ phức tạp của mã thực sự được phân chia vì mã ít đậm đặc hơn.
Trong khi đọc mã, bạn không cần phải nhớ bất cứ điều gì về các điều kiện trong quá khứ: nếu bạn đang ở điểm X trong mã, các bước trước đó đã được thông qua và đã thành công.
Một lợi ích khác là "đường thoát và điều kiện" từ tất cả những "if-other" lồng nhau được đơn giản hóa.