Da dove viene l'idea di "un solo ritorno"?


1056

Parlo spesso con i programmatori che dicono " Non mettere più dichiarazioni di ritorno nello stesso metodo " . Quando chiedo loro di dirmi i motivi, tutto quello che ottengo è " Lo standard di codifica lo dice " o " È confuso " . Quando mi mostrano soluzioni con una singola dichiarazione di ritorno, il codice mi sembra più brutto. Per esempio:

if (condition)
   return 42;
else
   return 97;

" È brutto, devi usare una variabile locale! "

int result;
if (condition)
   result = 42;
else
   result = 97;
return result;

In che modo questo bloat del codice del 50% semplifica la comprensione del programma? Personalmente, lo trovo più difficile, perché lo spazio degli stati è appena aumentato di un'altra variabile che avrebbe potuto essere facilmente evitato.

Certo, normalmente scriverei solo:

return (condition) ? 42 : 97;

Ma molti programmatori evitano l'operatore condizionale e preferiscono la forma lunga.

Da dove viene questa nozione di "un solo ritorno"? C'è una ragione storica per cui è nata questa convenzione?


2
Questo è in qualche modo collegato al refactoring della clausola di guardia. stackoverflow.com/a/8493256/679340 La clausola di guardia aggiungerà ritorni all'inizio dei metodi. E rende il codice molto più pulito secondo me.
Piotr Perak,

3
Deriva dal concetto di programmazione strutturata. Alcuni potrebbero sostenere che avere un solo ritorno consente di modificare facilmente il codice per fare qualcosa prima di tornare o eseguire facilmente il debug.
martinkunev,

3
penso che l'esempio sia un caso abbastanza semplice in cui non avrei un'opinione forte in un modo o nell'altro. l'ideale single-entry-single-exit è più per guidarci lontano da situazioni folli come 15 dichiarazioni di ritorno e altri due rami che non ritornano affatto!
mendota,

2
Questo è uno dei peggiori articoli che abbia mai letto. Sembra che l'autore passi più tempo a fantasticare sulla purezza del suo OOP piuttosto che a capire come ottenere qualcosa. Gli alberi di espressione e valutazione hanno un valore, ma non quando si può semplicemente scrivere una normale funzione.
DeadMG

3
È necessario rimuovere completamente la condizione. La risposta è 42.
cambunctious

Risposte:


1120

"Single Entry, Single Exit" è stato scritto quando la maggior parte della programmazione è stata eseguita in linguaggio assembly, FORTRAN o COBOL. È stato ampiamente frainteso, perché le lingue moderne non supportano le pratiche contro cui Dijkstra stava mettendo in guardia.

"Voce singola" significa "non creare punti di entrata alternativi per le funzioni". Nel linguaggio assembly, ovviamente, è possibile inserire una funzione in qualsiasi istruzione. FORTRAN ha supportato più voci alle funzioni con l' ENTRYistruzione:

      SUBROUTINE S(X, Y)
      R = SQRT(X*X + Y*Y)
C ALTERNATE ENTRY USED WHEN R IS ALREADY KNOWN
      ENTRY S2(R)
      ...
      RETURN
      END

C USAGE
      CALL S(3,4)
C ALTERNATE USAGE
      CALL S2(5)

"Uscita singola" significa che una funzione dovrebbe tornare in un solo posto: la dichiarazione immediatamente successiva alla chiamata. Ciò non significava che una funzione dovesse tornare da un solo posto. Quando è stata scritta la Programmazione strutturata , era prassi comune per una funzione indicare un errore ritornando in una posizione alternativa. FORTRAN ha supportato questo tramite "ritorno alternativo":

C SUBROUTINE WITH ALTERNATE RETURN.  THE '*' IS A PLACE HOLDER FOR THE ERROR RETURN
      SUBROUTINE QSOLVE(A, B, C, X1, X2, *)
      DISCR = B*B - 4*A*C
C NO SOLUTIONS, RETURN TO ERROR HANDLING LOCATION
      IF DISCR .LT. 0 RETURN 1
      SD = SQRT(DISCR)
      DENOM = 2*A
      X1 = (-B + SD) / DENOM
      X2 = (-B - SD) / DENOM
      RETURN
      END

C USE OF ALTERNATE RETURN
      CALL QSOLVE(1, 0, 1, X1, X2, *99)
C SOLUTION FOUND
      ...
C QSOLVE RETURNS HERE IF NO SOLUTIONS
99    PRINT 'NO SOLUTIONS'

Entrambe queste tecniche erano fortemente soggette a errori. L'uso di voci alternative spesso lasciava alcune variabili non inizializzate. L'uso di ritorni alternativi presentava tutti i problemi di un'istruzione GOTO, con l'ulteriore complicazione che la condizione del ramo non era adiacente al ramo, ma da qualche parte nella subroutine.


39
E non dimenticare il codice spaghetti . Non era sconosciuto che le subroutine uscissero usando un GOTO invece di un ritorno, lasciando i parametri della chiamata di funzione e l'indirizzo di ritorno nello stack. L'uscita singola è stata promossa come un modo per incanalare almeno tutti i percorsi del codice in un'istruzione RETURN.
TMN,

2
@TMN: all'inizio, la maggior parte delle macchine non aveva uno stack hardware. La ricorsione generalmente non era supportata. Gli argomenti della subroutine e l'indirizzo di ritorno sono stati memorizzati in posizioni fisse adiacenti al codice della subroutine. Il ritorno era solo un goto indiretto.
Kevin Cline,

5
@kevin: Sì, ma secondo te questo non significa nemmeno più come è stato inventato. (A proposito, in realtà sono ragionevolmente sicuro che Fred abbia chiesto la preferenza per l' interpretazione attuale di "Uscita singola".) Inoltre, C ha avuto constda prima che molti degli utenti qui nascessero, quindi non c'è più bisogno di costanti di capitale anche in C. Ma Java conservato tutte quelle cattive vecchie abitudini C .
sabato

3
Quindi le eccezioni violano questa interpretazione dell'uscita singola? (O il loro cugino più primitivo,? setjmp/longjmp)
Mason Wheeler,

2
Anche se l'op ha chiesto l'attuale interpretazione del ritorno singolo, questa risposta è quella con le radici più storiche. Non ha senso usare una singola restituzione come regola , a meno che tu non voglia che la tua lingua corrisponda alla bellezza di VB (non .NET). Ricorda di usare anche la logica booleana non a corto circuito.
acelent

912

Questa nozione di Single Entry, Single Exit (SESE) proviene da lingue con gestione esplicita delle risorse , come C e assembly. In C, codice come questo perderà risorse:

void f()
{
  resource res = acquire_resource();  // think malloc()
  if( f1(res) )
    return; // leaks res
  f2(res);
  release_resource(res);  // think free()
}

In tali lingue, hai sostanzialmente tre opzioni:

  • Replica il codice di pulizia.
    Ugh. La ridondanza è sempre negativa.

  • Utilizzare a gotoper saltare al codice di pulizia.
    Ciò richiede che il codice di pulizia sia l'ultima cosa nella funzione. (E questo è il motivo per cui alcuni sostengono che gotoabbia il suo posto. E in effetti ha - in C.)

  • Introdurre una variabile locale e manipolare il flusso di controllo attraverso quello.
    Lo svantaggio è che il flusso di controllo manipolato attraverso la sintassi (si pensi break, return, if, while) è molto più facile da seguire rispetto flusso di controllo manipolato attraverso lo stato di variabili (perché quelle variabili non hanno alcun stato in cui si guarda l'algoritmo).

Nell'assemblaggio è ancora più strano, perché puoi passare a qualsiasi indirizzo in una funzione quando chiami quella funzione, il che significa che in realtà hai un numero quasi illimitato di punti di ingresso per qualsiasi funzione. (A volte questo è utile. Tali thunk sono una tecnica comune per i compilatori per implementare la thisregolazione del puntatore necessaria per chiamare le virtualfunzioni in scenari di ereditarietà multipla in C ++.)

Quando è necessario gestire le risorse manualmente, lo sfruttamento delle opzioni di immissione o chiusura di una funzione ovunque porta a un codice più complesso e quindi a bug. Pertanto, apparve una scuola di pensiero che propagò SESE, al fine di ottenere un codice più pulito e meno bug.


Tuttavia, quando una lingua presenta eccezioni, (quasi) qualsiasi funzione potrebbe essere abbandonata prematuramente in (quasi) qualsiasi punto, quindi è necessario prevedere comunque un ritorno prematuro. (Penso che finallysia usato principalmente per quello in Java e using(durante l'implementazione IDisposable, finallyaltrimenti) in C #; C ++ invece utilizza RAII .) Una volta fatto questo, non puoi non ripulire dopo te stesso a causa di una prima returndichiarazione, quindi probabilmente l'argomento più forte a favore di SESE è svanito.

Ciò lascia leggibilità. Naturalmente, una funzione 200 LoC con una mezza dozzina di returnistruzioni sparse casualmente su di essa non è un buon stile di programmazione e non rende il codice leggibile. Ma una tale funzione non sarebbe facile da capire senza quei ritorni prematuri.

Nelle lingue in cui le risorse non sono o non devono essere gestite manualmente, aderire alla vecchia convenzione SESE ha poco o nessun valore. OTOH, come ho affermato sopra, SESE spesso rende il codice più complesso . È un dinosauro che (tranne C) non si adatta bene alla maggior parte delle lingue di oggi. Invece di aiutare la comprensibilità del codice, lo ostacola.


Perché i programmatori Java si attengono a questo? Non lo so, ma dal mio POV (esterno), Java ha preso molte convenzioni da C (dove hanno un senso) e le ha applicate al suo mondo OO (dove sono inutili o decisamente cattive), dove ora si attacca loro, non importa quali siano i costi. (Come la convenzione per definire tutte le variabili all'inizio dell'ambito.)

I programmatori si attengono a tutti i tipi di strane notazioni per motivi irrazionali. (Dichiarazioni strutturali profondamente annidate - "punte di freccia" - erano, in linguaggi come Pascal, un tempo viste come un bel codice.) Applicare un ragionamento logico puro a questo sembra non riuscire a convincere la maggior parte di loro a deviare dai loro modi stabiliti. Il modo migliore per cambiare tali abitudini è probabilmente insegnare loro presto a fare ciò che è meglio, non ciò che è convenzionale. Tu, essendo un insegnante di programmazione, ce l'hai in mano.:)


52
Giusto. In Java, il codice di pulizia appartiene a finallyclausole in cui viene eseguito indipendentemente dalle prime returns o eccezioni.
dan04

15
@ dan04 in Java 7 non ti serve nemmeno il finallypiù delle volte.
R. Martinho Fernandes,

93
@Steven: certo che puoi dimostrarlo! In effetti, puoi mostrare codice complesso e contorto con qualsiasi funzione che può anche essere mostrata per rendere il codice più semplice e più comprensibile. Tutto può essere abusato. Il punto è scrivere codice in modo che sia più facile da capire , e quando ciò implica buttare SESE fuori dalla finestra, così sia, e dannazione per le vecchie abitudini che si applicavano a lingue diverse. Ma non esiterei a controllare l'esecuzione tramite variabili, anche se penso che abbia reso più semplice la lettura del codice. È solo che non ricordo di aver visto un simile codice in quasi due decenni.
sabato

21
@Karl: In effetti, è un grave difetto dei linguaggi GC come Java che ti sollevano dal dover ripulire una risorsa, ma falliscono con tutte le altre. (C ++ risolve questo problema per tutte le risorse utilizzando Raii .) Ma non era nemmeno parlando di memoria di sola (ho messo solo malloc()e free()in un commento come esempio), io parlavo di risorse in generale. Inoltre non intendevo che GC avrebbe risolto questi problemi. (Ho citato C ++, che non ha GC pronto all'uso.) Da quello che ho capito, in Java finallyviene utilizzato per risolvere questo problema.
sabato

10
@sbi: più importante per una funzione (procedura, metodo, ecc.) che non essere più lungo di una pagina è che la funzione abbia un contratto chiaramente definito; se non sta facendo qualcosa di chiaro perché è stato tagliato per soddisfare un vincolo di lunghezza arbitrario, è Bad. La programmazione consiste nel giocare forze diverse, a volte contrastanti tra loro.
Donal Fellows,

81

Da un lato, le singole dichiarazioni di restituzione semplificano la registrazione, nonché le forme di debug che si basano sulla registrazione. Ricordo molte volte che ho dovuto ridurre la funzione in un singolo ritorno solo per stampare il valore di ritorno in un singolo punto.

  int function() {
     if (bidi) { print("return 1"); return 1; }
     for (int i = 0; i < n; i++) {
       if (vidi) { print("return 2"); return 2;}
     }
     print("return 3");
     return 3;
  }

D'altra parte, è possibile includere questo fattore in function()quelle chiamate _function()e registrare il risultato.


31
Vorrei anche aggiungere che semplifica il debug perché è necessario impostare un solo punto di interruzione per catturare tutte le uscite * dalla funzione. Sono convinto che alcuni IDE ti consentano di mettere un punto di interruzione sul controvento stretto della funzione per fare la stessa cosa. (* a meno che non chiami exit)
Skizz,

3
Per un motivo simile, semplifica anche l'estensione (aggiunta a) della funzione, poiché non è necessario inserire la nuova funzionalità prima di ogni ritorno. Supponi di dover aggiornare un registro con il risultato della chiamata di funzione, ad esempio.
JeffSahol,

63
Onestamente, se mantenessi quel codice, preferirei avere una definizione sensata _function(), con returns in punti appropriati, e un wrapper chiamato function()che gestisca la registrazione estranea, piuttosto che avere un singolo function()con logica contorta per far rientrare tutti i ritorni in una singola uscita punto solo per poter inserire un'istruzione aggiuntiva prima di quel punto.
Ruakh,

11
In alcuni debugger (MSVS) è possibile inserire il punto di interruzione nell'ultima parentesi graffa di chiusura
Abyx,

6
stampa! = debug. Questo non è affatto argomento.
Piotr Perak,

53

"Single Entry, Single Exit" è nato con la rivoluzione della Programmazione strutturata dei primi anni '70, che è stata lanciata dalla lettera di Edsger W. Dijkstra all'editore " Dichiarazione GOTO considerata dannosa ". I concetti alla base della programmazione strutturata sono stati dettagliati nel libro classico "Programmazione strutturata" di Ole Johan-Dahl, Edsger W. Dijkstra e Charles Anthony Richard Hoare.

È necessario leggere "Dichiarazione GOTO considerata dannosa" anche oggi. La "Programmazione strutturata" è datata, ma comunque molto, molto gratificante e dovrebbe essere in cima alla lista "Must Read" di ogni sviluppatore, molto al di sopra di qualsiasi cosa, ad esempio Steve McConnell. (La sezione di Dahl espone le basi delle lezioni in Simula 67, che sono le basi tecniche per le lezioni in C ++ e tutta la programmazione orientata agli oggetti.)


6
L'articolo è stato scritto pochi giorni prima di C quando GOTO era usato pesantemente. Non sono nemici, ma questa risposta è decisamente corretta. Un'istruzione return che non è alla fine di una funzione è effettivamente un goto.
user606723

31
L'articolo è stato anche scritto nei giorni in cui gotopoteva letteralmente andare ovunque , come in un punto casuale in un'altra funzione, bypassando qualsiasi nozione di procedure, funzioni, uno stack di chiamate, ecc. Nessun linguaggio sano permette che in questi giorni con una scala goto. C setjmp/ longjmpè l'unico caso semi-eccezionale di cui sono a conoscenza, e anche questo richiede una cooperazione da entrambe le parti. (Semi-ironico che ho usato la parola "eccezionale" lì, però, considerando che le eccezioni fanno quasi la stessa cosa ...) Fondamentalmente, l'articolo scoraggia una pratica che è morta da tempo.
cHao,

5
Dall'ultimo paragrafo di "Dichiarazione di Goto considerata dannosa": "in [2] Guiseppe Jacopini sembra aver dimostrato la (logica) superfluità dell'istruzione go to. L'esercizio per tradurre un diagramma di flusso arbitrario più o meno meccanicamente in un salto- meno uno, tuttavia, non è raccomandato . Quindi il diagramma di flusso risultante non può essere più trasparente di quello originale. "
hugomg

10
Cosa c'entra questo con la domanda? Sì, il lavoro di Dijkstra alla fine ha portato alle lingue SESE, e allora? Così ha fatto il lavoro di Babbage. E forse dovresti rileggere il documento se pensi che dica qualcosa sull'avere più punti di uscita in una funzione. Perché no.
jalf

10
@Giovanni, sembra che tu stia cercando di rispondere alla domanda senza rispondere. È un buon elenco di letture, ma non hai né citato né parafrasato nulla per giustificare la tua affermazione che questo saggio e questo libro hanno qualcosa da dire sulla preoccupazione del richiedente. In effetti, al di fuori dei commenti non hai detto nulla di sostanziale sulla domanda. Valuta di espandere questa risposta.
Shog9

35

È sempre facile collegare Fowler.

Uno dei principali esempi che vanno contro SESE sono le clausole di guardia:

Sostituisci condizionale nidificato con clausole di guardia

Usa le clausole di guardia per tutti i casi speciali

double getPayAmount() {
    double result;
    if (_isDead) result = deadAmount();
    else {
        if (_isSeparated) result = separatedAmount();
        else {
            if (_isRetired) result = retiredAmount();
            else result = normalPayAmount();
        };
    }
return result;
};  

                                                                                                         http://www.refactoring.com/catalog/arrow.gif

double getPayAmount() {
    if (_isDead) return deadAmount();
    if (_isSeparated) return separatedAmount();
    if (_isRetired) return retiredAmount();
    return normalPayAmount();
};  

Per ulteriori informazioni, vedere pagina 250 di Refactoring ...


11
Un altro cattivo esempio: potrebbe essere risolto altrettanto facilmente con else-ifs.
Jack,

1
Il tuo esempio non è giusto, che ne dici di questo: double getPayAmount () {double ret = normalPayAmount (); if (_isDead) ret = deadAmount (); if (_isSeparated) ret = separateAmount (); if (_isRetired) ret = retiredAmount (); ritorno ret; };
Charbel,

6
@Charbel Non è la stessa cosa. Se _isSeparatede _isRetiredpuò essere vero (e perché non sarebbe possibile?) Restituisci l'importo errato.
hvd,

2
@Konchog "I condizionali nidificati forniranno un tempo di esecuzione migliore rispetto alle clausole di guardia " Ciò richiede soprattutto una citazione. Dubito che sia assolutamente vero. In questo caso, ad esempio, in che modo i rendimenti iniziali sono diversi dai cortocircuiti logici in termini di codice generato? Anche se importava, non riesco a immaginare un caso in cui la differenza sarebbe più di un nastro infinitesimale. Quindi stai applicando l'ottimizzazione prematura rendendo il codice meno leggibile, solo per soddisfare alcuni punti teorici non dimostrati su ciò che pensi porti a un codice leggermente più veloce. Non lo facciamo qui
underscore_d

1
@underscore_d, hai ragione. dipende molto dal compilatore, ma può richiedere più spazio. Guarda i due pseudo-assemblaggi ed è facile capire perché le clausole di guardia provengono da linguaggi di alto livello. Test "A" (1); branch_fail end; Test (2); branch_fail end; test (3); branch_fail end; {CODICE} fine: ritorno; Test "B" (1); branch_good next1; ritorno; next1: test (2); branch_good next2; ritorno; next2: test (3); branch_good next3; ritorno; next3: {CODICE} ritorno;
Konchog,

11

Ho scritto un post sul blog su questo argomento qualche tempo fa.

La linea di fondo è che questa regola deriva dall'età delle lingue che non hanno la garbage collection o la gestione delle eccezioni. Non esiste uno studio formale che dimostri che questa regola porta a un codice migliore nelle lingue moderne. Sentiti libero di ignorarlo ogni volta che questo porterà a un codice più breve o più leggibile. I ragazzi Java che insistono su questo sono ciecamente e senza dubbio a seguito di una regola obsoleta e inutile.

Questa domanda è stata posta anche su StackOverflow


Ehi, non riesco più a raggiungere quel link. Ti capita di avere una versione ospitata da qualche parte ancora accessibile?
Nic Hartley,

Ciao, QPT, buon posto. Ho riportato il post sul blog e aggiornato l'URL sopra. Dovrebbe collegarsi ora!
Anthony,

C'è di più, però. È molto più facile gestire i tempi di esecuzione precisi utilizzando SESE. I condizionali nidificati possono spesso essere rifattorizzati con un interruttore comunque. Non si tratta solo di stabilire se esiste o meno un valore di ritorno.
Konchog,

Se hai intenzione di affermare che non esiste uno studio formale a sostegno di ciò, ti spetterebbe a collegarti a uno che va contro di esso.
Mehrdad,

Mehrdad, se esiste uno studio formale a supporto di ciò, mostralo. È tutto. Insistere sulle prove contro sta spostando l'onere della prova.
Anthony,

7

Un ritorno semplifica il refactoring. Prova a eseguire il "metodo extract" nel corpo interno di un ciclo for che contiene return, break o continue. Ciò fallirà poiché hai interrotto il flusso di controllo.

Il punto è: suppongo che nessuno stia fingendo di scrivere un codice perfetto. Quindi il codice è regolarmente sotto refactoring per essere "migliorato" ed esteso. Quindi il mio obiettivo sarebbe quello di mantenere il mio codice il più amichevole possibile per il refactoring.

Spesso incontro il problema che devo riformulare completamente le funzioni se contengono interruttori di flusso di controllo e se voglio aggiungere solo poche funzionalità. Questo è molto soggetto a errori quando si modifica l'intero flusso di controllo invece di introdurre nuovi percorsi in nidificazioni isolate. Se alla fine hai un solo ritorno o se usi guardie per uscire da un ciclo, ovviamente hai più nidificazione e più codice. Ma ottieni capacità di refactoring supportate dal compilatore e dall'IDE.


Lo stesso vale per le variabili. Quali sono l'alternativa all'utilizzo di costrutti di controllo-flusso come il ritorno anticipato.
Deduplicatore,

Le variabili per lo più non ti impediranno di dividere il codice in pezzi in modo da preservare il flusso di controllo esistente. Prova "estrai metodo". Gli IDE sono in grado di eseguire solo i refactoring di preserivazione del flusso di controllo in quanto non sono in grado di derivare la semantica da ciò che hai scritto.
oopexpert,

5

Considera il fatto che più dichiarazioni di reso equivalgono ad avere GOTO per una singola dichiarazione di reso. Questo è lo stesso caso con le dichiarazioni di rottura. Pertanto, alcuni, come me, li considerano GOTO a tutti gli effetti.

Tuttavia, non considero dannosi questi tipi di GOTO e non esiterò a utilizzare un GOTO reale nel mio codice se trovo una buona ragione per farlo.

La mia regola generale è che i GOTO sono solo per il controllo del flusso. Non dovrebbero mai essere usati per alcun loop e non dovresti mai GOTO "verso l'alto" o "indietro". (che funziona come interruzioni / ritorni)

Come altri hanno già detto, è assolutamente necessario leggere la Dichiarazione GOTO considerata dannosa
Tuttavia, tenere presente che questo è stato scritto nel 1970 quando i GOTO erano troppo abusati. Non tutti i GOTO sono dannosi e non scoraggerei il loro uso finché non li usi al posto dei costrutti normali, ma piuttosto nel caso strano che l'uso di costrutti normali sarebbe altamente scomodo.

Trovo che usarli in casi di errore in cui è necessario uscire da un'area a causa di un errore che non dovrebbe mai verificarsi in casi normali sia utile a volte. Ma dovresti anche considerare di mettere questo codice in una funzione separata in modo da poter semplicemente tornare presto invece di usare un GOTO ... ma a volte è anche scomodo.


6
Tutti i costrutti strutturati che sostituiscono i goto sono implementati in termini di goto. Ad esempio loop, "if" e "case". Questo non li rende cattivi, anzi il contrario. Inoltre, si tratta di "intenti e scopi".
Anthony,

Touche, ma questo non differisce dal mio punto di vista ... Rende solo leggermente sbagliata la mia spiegazione. Oh bene.
user606723

GOTO dovrebbe essere sempre ok fintanto che (1) target è all'interno dello stesso metodo o funzione e (2) la direzione è in avanti nel codice (salta un po 'di codice) e (3) il target non è all'interno di un'altra struttura nidificata (ad es. GOTO dal centro dell'if-case al centro dell'altro caso). Se segui queste regole, tutti gli abusi di GOTO hanno un odore di codice davvero forte sia visivamente che logicamente.
Mikko Rantalainen,

3

Complessità ciclomatica

Ho visto SonarCube utilizzare una dichiarazione di ritorno multipla per determinare la complessità ciclomatica. Più sono le dichiarazioni di ritorno, maggiore è la complessità ciclomatica

Modifica tipo di ritorno

Restituzioni multiple significano che dobbiamo cambiare in più punti della funzione quando decidiamo di cambiare il nostro tipo di restituzione.

Uscita multipla

È più difficile eseguire il debug poiché la logica deve essere attentamente studiata insieme alle istruzioni condizionali per capire cosa ha causato il valore restituito.

Soluzione refactored

La soluzione a più dichiarazioni di ritorno è di sostituirle con il polimorfismo avere un unico ritorno dopo aver risolto l'oggetto di implementazione richiesto.


3
Passare da più ritorni a impostare il valore di ritorno in più punti non elimina la complessità ciclomatica, unifica solo la posizione di uscita. Rimangono tutti i problemi che la complessità ciclomatica può indicare in un determinato contesto. "È più difficile eseguire il debug poiché la logica deve essere attentamente studiata insieme alle istruzioni condizionali per capire cosa ha causato il valore restituito" Ancora una volta, la logica non cambia unificando il ritorno. Se devi studiare attentamente il codice per capire come funziona, deve essere sottoposto a refactoring, punto e basta.
WillD
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.