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 goto
per 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 goto
abbia 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 this
regolazione del puntatore necessaria per chiamare le virtual
funzioni 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 finally
sia usato principalmente per quello in Java e using
(durante l'implementazione IDisposable
, finally
altrimenti) in C #; C ++ invece utilizza RAII .) Una volta fatto questo, non puoi non ripulire dopo te stesso a causa di una prima return
dichiarazione, quindi probabilmente l'argomento più forte a favore di SESE è svanito.
Ciò lascia leggibilità. Naturalmente, una funzione 200 LoC con una mezza dozzina di return
istruzioni 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.:)