Questa nozione di Single Entry, Single Exit (SESE) deriva da lingue con gestione delle risorse esplicite , 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, in pratica hai tre opzioni:
-
Replica il codice di pulizia.
Ugh. La ridondanza è sempre cattiva.
-
Utilizza un goto
per passare al codice cleanup.
Ciò richiede che il codice di pulizia sia l'ultima cosa nella funzione. (E questo è il motivo per cui alcuni sostengono che goto
ha il suo posto ed ha effettivamente - in C.)
-
Introdurre una variabile locale e manipolare il flusso di controllo attraverso questo.
Lo svantaggio è che il flusso di controllo manipolato tramite sintassi (pensa break
, return
, if
, while
) è molto più facile da seguire rispetto al flusso di controllo manipolato attraverso lo stato delle variabili (perché quelle variabili non hanno stato quando tu guarda all'algoritmo).
In assembly è anche più strano, perché puoi saltare a qualsiasi indirizzo in una funzione quando chiami quella funzione, il che significa in pratica avere un numero quasi illimitato di punti di ingresso per qualsiasi funzione. (Talvolta questo è utile. Questi thunk sono una tecnica comune per i compilatori per implementare la regolazione del puntatore this
necessaria per chiamare le funzioni virtual
negli scenari di ereditarietà multipla in C ++.)
Quando devi gestire le risorse manualmente, sfruttare le opzioni per entrare o uscire da una funzione ovunque porta a un codice più complesso, e quindi a bug. Pertanto, apparve una scuola di pensiero che propagava SESE, al fine di ottenere un codice più pulito e meno bug.
Tuttavia, quando una lingua presenta delle eccezioni, (quasi) qualsiasi funzione potrebbe essere abbandonata prematuramente a (quasi) qualsiasi punto, quindi è necessario fare comunque delle disposizioni per un ritorno prematuro. (Penso che finally
sia usato principalmente per quello in Java e using
(quando si implementa IDisposable
, finally
altrimenti) in C #; C ++ impiega invece RAII .) Una volta fatto ciò, non puoi non riuscire a ripulire dopo te stesso a causa di una precoce dichiarazione return
, quindi quello che è probabilmente l'argomento più strong in il favore di SESE è svanito.
Questo lascia la leggibilità. Naturalmente, una funzione di 200 LoC con una mezza dozzina di% di dichiarazioni di co_de 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 dovrebbero essere gestite manualmente, c'è poco o nessun valore nell'aderire alla vecchia convenzione SESE. OTOH, come ho sostenuto sopra, SESE spesso rende il codice più complesso . È un dinosauro che (ad eccezione di C) non si adatta bene alla maggior parte delle lingue odierne. Invece di aiutare la comprensibilità del codice, lo ostacola.
Perché i programmatori Java si limitano a questo? Non lo so, ma dal mio (fuori) POV, Java ha preso molte convenzioni da C (dove hanno senso) e le ha applicate al suo mondo OO (dove sono inutili o addirittura brutte), dove ora si attacca a loro, non importa quali siano i costi. (Come la convenzione per definire tutte le variabili all'inizio dello scope.)
I programmatori si attaccano a tutti i tipi di notazioni strane per ragioni irrazionali. (Affermazioni strutturali profondamente annidate - "punte di freccia" - erano, in lingue come Pascal, una volta viste come codice bello.) Applicare un puro ragionamento logico a questo sembra non riuscire a convincere la maggioranza di loro a deviare dai loro modi stabiliti. Il modo migliore per cambiare tali abitudini è probabilmente quello di insegnare loro presto a fare ciò che è meglio, non ciò che è convenzionale. Tu, essendo un insegnante di programmazione, ce l'hai in mano. return