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

17

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

Tamara Wijsman
źródło
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.

Chinmay Kanchi
źródło
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);
    }

    // ....
}
ChaosPandion
źródło
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

Jason Turan
źródło
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.

Konrad Morawski
źródło
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.

Vaibhav
źródło
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.

Hoa Long Tam
źródło
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
Paul Nathan
źródło
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.

Peter Turner
źródło
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.

Pierre Watelet
źródło
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