Jak zapobiegać głębokim wgnieceniom? [Zamknięte]


17

Jakie kroki i środki mogę podjąć, aby zapobiec głębokim wcięciom w moim kodzie?


2
Wiele osób będzie tutaj mówić o refaktoryzacji. Być może jest to zbyt wiele, by o to zapytać, ale jeśli opublikowałeś (niezbyt długi) kod, który jest głęboko wcięty, a ludzie mogą pokazać ci, jak go zmienili. Oczywiście to prawdopodobnie sprawia, że ​​język pytań jest wtedy specyficzny ...
— Paddyslacker

3
Użyj mniejszej szerokości zakładki.
— mipadi

6
Arrowhead anty-wzór. Google to, mnóstwo wskazówek
— NimChimpsky

2
Przestań używać Pythona: D
— back2dos

Czas spojrzeć na swoją kontrolę i logikę pętli. Prawdopodobnie Twój kod jest bardziej skomplikowany niż powinien, a ponowna konceptualizacja problemu doprowadzi do znacznie krótszego kodu. Studiuj dobry kod i ucz się technik.
— Macneil

Odpowiedzi:


14

Głębokie wcięcie zwykle nie stanowi problemu, jeśli każda funkcja / metoda w twoim programie wykonuje jedną i tylko jedną rzecz. Czasami może być konieczne zagnieżdżenie warunków na kilku poziomach głębokości, ale mogę szczerze powiedzieć, że napisałem głęboko wcięty kod tylko kilka razy w ciągu ponad 12 lat kodowania.


26

Najlepsze, co możesz zrobić, to wyodrębnić metody:

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
Działa to również w przypadku złożonych ifwarunków. Podsumowując, skończysz na pseudokodzie wykonywalnym.
— Alan Plum,

Inną najlepszą rzeczą, jaką możemy zrobić, to prawdopodobnie zrzucić niepotrzebne elsebloki.
— sepehr

16

Może mógłbyś rozważyć klauzule ochronne ?

zamiast

public void DoSomething(int value){
    if (someCondition){
           if(someOtherCondition){
                if(yetAnotherCondition){
                       //Finally execute some code
                }
           }
    }
} 

Robić

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
}

Jeśli kiedykolwiek będziesz miał okazję, polecam przeczytanie Code Complete autorstwa Steve'a McConnella. Ma wiele świetnych porad na te tematy.

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

Aby uzyskać więcej informacji na temat „klauzul ochronnych”, patrz: https://sourcemaking.com/refactoring/replace-nested-conditional-with-guard-clauses


8

Odwróć swoje if.

Zamiast:

if (foo != null)
{
    something;
    something;
    if (x)
    {        
       something;
    }
    something;
}
else
{
    boohoo;
}

Napisałbym:

if (foo == null)
{
    boohoo;
    return;
}
something;
something;
if (x)
{        
   something;
}
something;

To samo dotyczy if- elsebloków. Jeśli elsejest krótszy / mniej zagnieżdżony, przywróć je.

Sprawdź wartości parametrów w jednym miejscu

Po wprowadzeniu metody sprawdź wszystkie parametry pod kątem niedozwolonych wartości, a następnie kontynuuj, wiedząc, że jesteś bezpieczny. To sprawia, że ​​kod jest bardziej czytelny, ale oszczędza również później gromadzenia bloków warunkowych i rozprowadzania tych kontroli po całym podprogramie.


1
Czy ten styl ma określoną nazwę?
— Thomas Lauria,

@ThomasLauria nie, żebym wiedział. Po prostu wychodzi wcześnie. IfNa początku kodu, który zatrzymuje przepływ wykonywania z powodu niespełnienia niektórych warunków, znane są również jako klauzule ochronne , jak wskazał @JasonTuran. I wydaje się, że jest tak blisko, jak to możliwe, by mieć odrębne imię.
— Konrad Morawski

kilka lat temu mój przełożony powiedział mi, że styl ten nazwano „programowaniem liniowym”, ale myślę, że to był jego fantazmat;)
— Thomas Lauria

4

Zazwyczaj widziałem, że głęboko wcięty kod jest zwykle kodem problematycznym. Jeśli napotykasz ten problem, cofnij się i oceń, czy twoja funkcja robi zbyt wiele rzeczy.

Jednocześnie, aby odpowiedzieć na twoje pytanie, jeśli istnieje potrzeba tak głębokiego wcięcia, proponuję, abyś pozwolił, aby tam było. Z tego prostego powodu, że w takim kodzie wcięcie będzie pomocne, ponieważ prawdopodobnie będzie to bardzo długi fragment kodu.


2

Podziel zagnieżdżone komponenty (szczególnie te powtarzane) na osobne funkcje (jest to łatwiejsze, jeśli twój język obsługuje zamykanie) lub zamień serię zagnieżdżonych pętli na rekurencję.

Również wcięcie dwóch spacji zamiast czterech.


5
Po przejściu do kroku zmiany szerokości zakładki masz poważne kłopoty ...
— Daenyth

Zakładki z dwoma spacjami są twarde ...
— David Thornley

6
Zmiana wcięcia to tylko sposób na ukrycie problemu, a nie rozwiązanie
— Murph,

1

Nie uważam głębokich wcięć za kategoryczny problem, który należy usunąć (ani nie uważam refaktoryzacji za prawdziwą odpowiedź na wszystko).

Zazwyczaj zamiast zagnieżdżonych ifs lubię pisać logiczne instrukcje:

if (foo && bar && baz) 

zamiast

if foo 
 if bar
   if baz

Problem polega na tym, że istnieją również pętle, które nie podlegają tej regule.
— Tamara Wijsman,

@TomWij: Nie próbuję wywoływać kategorycznego imperatywu dotyczącego stylu.
— Paul Nathan

1
??? `` ``
— Tamara Wijsman

1

Sam w to nie wierzyłem, ale według Code Complete jest to odpowiednie miejsce do użycia break(jeśli twój zespół jest na pokładzie). Wyobrażam sobie, że jest to bardziej akceptowalne w przypadku programistów C ++, które są breakużywane w switchinstrukcjach, niż w przypadku programistów Delphi, gdzie breaksą używane tylko wtedy, gdy nie masz ochoty pisać whilepętli.


0

Wcięcie to naprawdę myśl do walki. Nauczyłem się, jak najpierw podzielić metodę na części, a następnie użyć dziwnej sztuczki, aby pominąć kolejne części, jeśli jeden element się nie powiedzie. Oto przykład :

Zamiast :

 {if (networkCardIsOn() == true)
     {if (PingToServer() == true)
        {if (AccesLogin(login,pass) == true)
             {if (nextCondition == true)
                ...
         }
     }
 }

Obecnie piszę:

 {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();
       } 
 }
 ...

Z początku wydaje mi się to dziwne, ale odkąd go używam, koszty utrzymania zostały podzielone na pół, a mój mózg na koniec dnia jest chłodniejszy.

W rzeczywistości korzyści wynikające z tej „techniki” polegają na tym, że złożoność kodu jest naprawdę podzielona, ​​ponieważ kod jest mniej gęsty.

Czytając kod, nie musisz pamiętać o przeszłych warunkach: jeśli znajdujesz się w punkcie X kodu, poprzednie kroki zostały pomyślnie zakończone.

Kolejną korzyścią jest to, że „ścieżka ucieczki i warunek” od wszystkich tych zagnieżdżonych „jeśli-inaczej” jest uproszczona.


Czy możesz rozwinąć kwestię „koszty utrzymania zostały podzielone przez połowę”. Ponadto, skąd naprawdę wiesz, które, gdyby wstrzymać wykonanie?
— Chris

Zrobiłem trochę edycji. Mam nadzieję, że odpowiem na twoje pytania ...
— Pierre Watelet,

2
Jeśli chcesz przejść nawet po prostu, masz linię obsługi błędów error_ goto.
— Paul Nathan

To nie jest miłe. Przepraszam, ale po prostu nie. Z drugiej strony jestem dziwny
— Murph,

3
zamiast naśladować try catch, dlaczego nie użyć bezpośrednio?
— Newtopian
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.