Penso che @JackAidley abbia già detto il succo , ma lascia che lo formuli in questo modo:
senza eccezioni (ad es. C)
Nel flusso di codice regolare, hai:
if (condition) {
statement;
} else if (less_likely_condition) {
less_likely_statement;
} else {
least_likely_statement;
}
more_statements;
Nel caso "error out early", il tuo codice improvvisamente legge:
/* demonstration example, do NOT code like this */
if (condition) {
statement;
} else {
error_handling;
return;
}
Se noti questo pattern - un return
in un blocco else
(o anche if
), rielaboralo immediatamente in modo che il codice in questione non abbia un blocco else
:
/* only code like this at University, to please structured programming professors */
function foo {
if (condition) {
lots_of_statements;
}
return;
}
Nel mondo reale ...
/* code like this instead */
if (!condition) {
error_handling;
return;
}
lots_of_statements;
Questo evita che il nesting sia troppo profondo e soddisfi il caso "break out early" (aiuta a mantenere la mente - e il flusso del codice - clean) e non violano la "metti la cosa più probabile nella parte if
" perché semplicemente non c'è nessuna parte else
.
C
e pulizia
Ispirato da una risposta a una domanda simile (che ha sbagliato), ecco come si pulisce con C. Puoi utilizzare uno o due punti di uscita lì, ecco uno per due punti di uscita:
struct foo *
alloc_and_init(size_t arg1, int arg2)
{
struct foo *res;
if (!(res = calloc(sizeof(struct foo), 1)))
return (NULL);
if (foo_init1(res, arg1))
goto err;
res.arg1_inited = true;
if (foo_init2(&(res->blah), arg2))
goto err;
foo_init_complete(res);
return (res);
err:
/* safe because we use calloc and false == 0 */
if (res.arg1_inited)
foo_dispose1(res);
free(res);
return (NULL);
}
Puoi comprimerli in un punto di uscita se c'è meno pulizia da fare:
char *
NULL_safe_strdup(const char *arg)
{
char *res = NULL;
if (arg == NULL)
goto out;
/* imagine more lines here */
res = strdup(arg);
out:
return (res);
}
Questo uso di goto
va perfettamente bene, se puoi gestirlo; il consiglio di stare fuori usando goto
è diretto a persone che non possono ancora decidere da soli se un uso è buono, accettabile, cattivo, codice spaghetti o qualcos'altro.
Eccezioni
Quanto sopra parla di lingue senza eccezioni, che preferisco di gran lunga me stesso (posso usare la gestione degli errori espliciti molto meglio e con molto meno sorpresa). Per citare igli:
<igli> exceptions: a truly awful implementation of quite a nice idea.
<igli> just about the worst way you could do something like that, afaic.
<igli> it's like anti-design.
<mirabilos> that too… may I quote you on that?
<igli> sure, tho i doubt anyone will listen ;)
Ma ecco un suggerimento su come farlo bene in una lingua con eccezioni e quando vuoi usarle bene:
errore di ritorno di fronte alle eccezioni
Puoi sostituire la maggior parte dei primi return
s generando un'eccezione. Tuttavia , il tuo flusso di programma normale , ovvero qualsiasi flusso di codice in cui il programma non ha incontrato, beh, un'eccezione ... una condizione di errore o somesuch, non aumenta eventuali eccezioni.
Questo significa che ...
# this page is only available to logged-in users
if not isLoggedIn():
# this is Python 2.5 style; insert your favourite raise/throw here
raise "eh?"
... va bene, ma ...
/* do not code like this! */
try {
openFile(xyz, "rw");
} catch (LockedException e) {
return "file is locked";
}
closeFile(xyz);
return "file is not locked";
... non lo è. Fondamentalmente, un'eccezione non è un elemento del flusso di controllo . Ciò inoltre rende le operazioni strane ("quei programmatori Java ™ ci dicono sempre che queste eccezioni sono normali") e possono ostacolare il debug (ad es. Dire all'IDE di interrompere qualsiasi eccezione). Le eccezioni spesso richiedono che l'ambiente runtime si distolga dallo stack per produrre traceback, ecc. Ci sono probabilmente più ragioni per non farlo.
Questo si riduce a: in una lingua che supporta le eccezioni, usa ciò che corrisponde alla logica e allo stile esistenti e ti sembra naturale. Se scrivi qualcosa da zero, prendi questo in anticipo. Se stai scrivendo una biblioteca da zero, pensa ai tuoi consumatori. (Non usare mai abort()
in una libreria ...) Ma qualsiasi cosa tu faccia, non fare come una regola generale un'eccezione generata se l'operazione continua (più o meno) normalmente dopo di essa.
consiglio generale wrt. Eccezioni
Cerca di ottenere tutto l'uso in programma delle eccezioni concordate prima dall'intero team di sviluppo. Fondamentalmente, programmali. Non usarli in abbondanza. A volte, anche in C ++, Java ™, Python, un errore di ritorno è migliore. A volte non lo è; usali con il pensiero.