Làm thế nào để ngăn ngừa vết lõm sâu? [đóng cửa]


17

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?


2
Rất nhiều người sẽ nói về tái cấu trúc ở đây. Có thể điều này là quá nhiều để hỏi, nhưng nếu bạn đăng một số mã (không quá dài) bị thụt sâu và mọi người có thể chỉ cho bạn cách họ tái cấu trúc nó. Tất nhiên, điều đó có thể làm cho ngôn ngữ câu hỏi trở nên cụ thể sau đó ...
Paddyslacker

3
Sử dụng chiều rộng tab nhỏ hơn.
mipadi

6
Mũi tên chống mẫu. Google nó, vô số lời khuyên
NimChimpsky

2
Ngừng sử dụng python: D
back2dos

Đã đến lúc xem xét điều khiển và vòng lặp logic của bạn. Có khả năng mã của bạn phức tạp hơn mức cần thiết và việc tái khái niệm hóa vấn đề sẽ dẫn đến mã ngắn hơn nhiều. Học mã tốt và học các kỹ thuật.
Macneil

Câu trả lời:


14

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, 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.


26

Đ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);
    }

    // ....
}

2
Điều này cũng hoạt động cho các 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.
Alan Plum

Điều tốt nhất khác chúng ta có thể làm là có thể bỏ elsecác khối không cần thiết .
sepehr

16

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.

http://www.amazon.com/Code-Complete-Practical-Handbook-Con Construction / dp / 0735619670 / ref = pd_sim_b_6

Để biết thêm về "mệnh đề bảo vệ", hãy xem: https://sourcemaking.com/refactoring/replace-nested-conditable-with-guard-clauses


8

Đảo ngược ifs 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- elsekhối. Nếu elsengắ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.


1
Liệu phong cách này có một tên cụ thể?
Thomas Lauria

@ThomasLauria không phải là điều mà tôi biết. Nó chỉ mới xuất hiện sớm thôi. Ifs ở đầ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.
Konrad Morawski

vài năm trước, người giám sát của tôi đã nói với tôi rằng phong cách này được đặt tên là "lập trình tuyến tính", nhưng tôi nghĩ rằng đây là một sự lừa đảo của anh ta;)
Thomas Lauria

4

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.


2

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.


5
Khi bạn đã đi đến bước thay đổi độ rộng tab của mình, bạn sẽ gặp rắc rối lớn ...
Daenyth

Các tab hai không gian là những thứ cứng ....
David Thornley

6
Thay đổi thụt lề chỉ là một cách để che giấu vấn đề không phải là một giải pháp
Murph

1

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

Vấn đề là, cũng tồn tại và trong khi các vòng lặp không thuộc quy tắc này.
Tamara Wijsman

@TomWij: Tôi không cố gắng tạo ra một mệnh lệnh phân loại về phong cách.
Paul Nathan

1
??? `` `` ``
Tamara Wijsman

1

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 switchcâu lệnh so với các lập trình viên Delphi, nơi breakchỉ được sử dụng khi bạn không cảm thấy muốn viết một whilevòng lặp.


0

Ấ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.


Bạn có thể nói rõ hơn về "chi phí bảo trì đã được chia cho một nửa" Ngoài ra, làm thế nào bạn thực sự biết điều gì nếu tạm dừng thực hiện?
Chris

Tôi đã thực hiện một số chỉnh sửa. Tôi hy vọng tôi trả lời câu hỏi của bạn ...
Pierre Watelet

2
Nếu bạn muốn đi thậm chí đơn giản, bạn có một dòng goto error_handling.
Paul Nathan

Điều đó không tốt. Xin lỗi, nhưng nó chỉ không. Sau đó, một lần nữa tôi thật kỳ lạ
Murph

3
thay vì giả lập một thử bắt tại sao không sử dụng trực tiếp?
Newtopian
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.