Come prevenire rientri profondi? [chiuso]


17

Quali passi e misure posso prendere per prevenire profonde rientranze nel mio codice?


2
Molte persone parleranno di refactoring qui. Forse questo è troppo da chiedere, ma se hai pubblicato un codice (non troppo lungo) che è profondamente rientrato, e le persone potrebbero mostrarti come lo farebbero refactoring. Certo, questo probabilmente rende la domanda specifica per il linguaggio quindi ...
Paddyslacker

3
Utilizzare una larghezza della scheda più piccola.
mipadi,

6
Arrowhead anti-pattern. Google it, un sacco di consigli
NimChimpsky

2
Smetti di usare python: D
back2dos

È tempo di esaminare il tuo controllo e la logica del loop. Probabilmente il tuo codice è più complicato del necessario e una riconcettualizzazione del problema porterà a un codice molto più breve. Studia il buon codice e apprendi le tecniche.
Macneil,

Risposte:


14

Il rientro profondo di solito non è un problema se ogni funzione / metodo nel tuo programma fa una sola cosa. Occasionalmente, potrebbe essere necessario nidificare i condizionali a pochi livelli di profondità, ma posso onestamente dire di aver scritto solo codice profondamente indentato una manciata di volte in oltre 12 anni di codifica.


26

La cosa migliore che puoi fare è estrarre i metodi:

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
Questo funziona anche per condizioni complesse if. Portato all'estremo, finirai con uno pseudocodice eseguibile.
Alan Plum,

Un'altra cosa migliore che possiamo fare è probabilmente eliminare elseblocchi non necessari .
sepehr,

16

Forse potresti prendere in considerazione le clausole di guardia ?

invece di

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

Fare

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
}

Se mai avessi la possibilità, ti consiglio di leggere Codice completo di Steve McConnell. Ha molti consigli su questi argomenti.

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

Per ulteriori informazioni sulle "clausole di guardia", consultare: https://sourcemaking.com/refactoring/replace-nested-conditional-with-guard-clauses


8

Invertire la tua ifs.

Invece di:

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

Scriverei:

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

Lo stesso vale per if- elseblocchi. Se elseè più breve / meno nidificato, ripristinalo.

Controlla i valori dei parametri in un unico posto

Controlla tutti i parametri per valori illegali non appena inserisci il tuo metodo, quindi procedi sapendo che sei al sicuro. Rende il codice più leggibile, ma ti salva anche accumulando blocchi condizionali in seguito e diffondendo questi controlli in tutta la subroutine.


1
Questo stile ha un nome specifico?
Thomas Lauria,

@ThomasLauria non che lo saprei. Sta uscendo presto. Ifs all'inizio del codice che interrompe il flusso di esecuzione a causa di una condizione non soddisfatta sono anche note come clausole di salvaguardia , come ha sottolineato @JasonTuran. E questo sembra essere il più vicino possibile ad avere un nome distinto.
Konrad Morawski

alcuni anni fa il mio supervisore mi disse che questo stile era chiamato "programmazione lineare", ma penso che questo fosse un suo fantasma;)
Thomas Lauria,

4

In genere, ho visto che il codice profondamente indentato è di solito un codice problematico. Se stai affrontando questo problema, fai un passo indietro e valuta se la tua funzione sta facendo troppe cose.

Allo stesso tempo, per rispondere alla tua domanda, se è necessario un rientro così profondo, ti suggerirei di lasciarlo lì. Per la semplice ragione che in tale codice, il rientro aiuterà poiché è probabilmente un pezzo di codice molto lungo.


2

Suddividere i componenti nidificati (specialmente quelli ripetuti) in funzioni separate (questo è più semplice se il linguaggio supporta le chiusure) o sostituire una serie di loop nidificati con una ricorsione.

Inoltre, rientra due spazi anziché quattro.


5
Una volta che sei passato alla fase di modifica della larghezza della tua scheda, sei in guai
seri

Le schede a due spazi sono le cose difficili ....
David Thornley,

6
Cambiare il rientro è solo un modo per nascondere il problema, non una soluzione
Murph

1

Non vedo le rientranze profonde come un problema categorico da rimuovere (né vedo il refactoring come la vera risposta a tutto).

In genere invece di if nidificati, mi piace scrivere istruzioni logiche:

if (foo && bar && baz) 

piuttosto che

if foo 
 if bar
   if baz

Il problema è che esistono anche per e mentre i loop che non rientrano in questa regola.
Tamara Wijsman,

@TomWij: non sto cercando di indurre un imperativo categorico riguardo allo stile.
Paul Nathan,

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

1

Non ci credevo, ma secondo Code Complete questo è un posto appropriato da usare break(se la tua squadra è a bordo). Immagino che questo sia più accettabile con i programmatori C ++, dove breakusato nelle switchistruzioni di quanto non lo sia con i programmatori Delphi dove breakviene usato solo quando non hai voglia di scrivere un whileciclo.


0

La rientranza è davvero un pensiero per combattere, anzi. Quello che ho imparato a fare è prima dividere il metodo in pezzi, quindi usare uno strano trucco per saltare tutti i pezzi successivi se un pezzo falliva. Ecco un esempio:

Invece di :

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

Attualmente scrivo:

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

All'inizio mi è sembrato strano, ma da quando lo uso, il costo di manutenzione è stato diviso per metà e il mio cervello è più freddo alla fine della giornata.

In effetti, il vantaggio introdotto da questa "tecnica" è che la complessità del codice è davvero divisa perché il codice è meno denso.

Durante la lettura del codice, non è necessario ricordare nulla delle condizioni passate: se ci si trova a quel punto X nel codice, i passaggi precedenti vengono passati e sono riusciti.

Un altro vantaggio è che il "percorso di fuga e condizione" da tutti quegli "if-else" nidificati è semplificato.


Puoi approfondire "il costo di manutenzione è stato diviso per metà" Inoltre, come faresti a sapere quale se interrompesse l'esecuzione?
Chris,

Ho fatto qualche modifica. Spero di rispondere alle tue domande ...
Pierre Watelet,

2
Se vuoi andare anche semplicemente, hai una goto error_handling line.
Paul Nathan,

Non è carino. Ci dispiace, ma non è così. Poi di nuovo sono strano
Murph,

3
invece di emulare un tentativo di cattura perché non utilizzarne uno direttamente?
Newtopian,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.