Perché molti programmatori sono passati a utilizzare la gestione delle eccezioni per l'input o l'output? Per questi programmatori, qual è la motivazione alla base di questa decisione?
Perché molti programmatori sono passati a utilizzare la gestione delle eccezioni per l'input o l'output? Per questi programmatori, qual è la motivazione alla base di questa decisione?
Le eccezioni sono, in parte, un riconoscimento che ci saranno condizioni che il software non può ragionevolmente recuperare da.
Le eccezioni sono il modo in cui il programmatore dice "Non posso continuare nelle condizioni attuali". In sostanza, quando si lancia un'eccezione, si arrende e si chiede un ripensamento.
Puoi farlo senza uscire con garbo dalla subroutine di input / output o anche preoccuparti della gestione della memoria, perché l'eliminazione ordinata degli oggetti rilevanti è già incorporata nel meccanismo di gestione delle eccezioni.
Puoi controllare dove nel codice viene gestita l'eccezione senza elaborate strutture if
, meccanismi di stato complessi o tracciamento della struttura delle chiamate.
Il codice tipico per le chiamate i / o prima delle eccezioni, di solito è stato scritto in termini di printf / scanf / gets / puts.
Queste funzioni restituiscono valori che consentono allo sviluppatore di verificare la presenza di errori, ma ciò non è banale.
La maggior parte degli sviluppatori, sapendo che la gestione degli errori non è banale, è semplicemente omessa; Tendono a pensare che una tipica chiamata fscanf dovrebbe apparire come questa:
int a;
FILE *f = /* ... */;
fscanf(f, "%d", &a);
quando dovrebbe probabilmente assomigliare a questo:
switch(fscanf(f, "%d", &a))
{
case EOF:
// todo: handle EOF case
break;
case 0:
puts("could not read 'a'");
break;
case 1:
// handle success case
break;
}
Questo codice può (a un punto) essere estratto in una funzione, parametrizzata e (probabilmente) nascosta all'interno di un wrapper su scanf, o una macro (entrambe con i lati negativi).
Se non lo scrivi, la tua applicazione semplicemente non gestisce gli errori. D'altra parte, le eccezioni ti danno più utili informazioni di errore, propagano naturalmente lo stack delle chiamate e non possono essere ignorate (se lo fai, loro fermeranno la tua applicazione).
Ad esempio (adoro gli esempi):
function myFunc(someInput:String) : OutputType
if firstErrorCondition(someInput)
throw new FirstConditionException
if secondErrorCondition(someInput)
throw new SecondConditionException
if thirdErrorCondition(someInput)
throw new ThirdConditionException
// do some other fancy stuff and return value eventually
Cosa succede se qui restituiremo un valore invece di generare eccezioni? Fammi vedere:
function myFunc(someInput:String) : OutputType
if firstErrorCondition(someInput)
return -1
if secondErrorCondition(someInput)
return -2
if thirdErrorCondition(someInput)
return -3
// do some other fancy stuff and return value eventually
Va bene, va ancora bene. Ma come apparirà il codice dei clienti?
OutputType result = myFunc(someInput)
Questa è una parte comune di questo. Eccezioni:
try {
OutputType result = myFunc(someInput)
//other operations
} catch (FirstErrorCondition e) {
//do some exception handling
} catch (SecondErrorCondition e) {
//do some magic
} catch (ThirdErrorCondition e) {
//or just inform user that is a problem
} finally {
//close everything and clean up after some doing
}
Come sviluppatore regolare posso vedere quale codice è responsabile per ogni parte di errore / eccezione.
OutputType result = myFunc(someInput)
switch(result) {
case -1:
//meh meh
case -2:
//meh meh
case -3:
//meh meh
default:
//and here we have a REGULAR code
}
Altro vantaggio delle eccezioni: può essere omesso nel codice client ma genererà un'eccezione alla fine quando si verifica un problema . Non sarà un codice silient, buggy e illeggibile.
Sono d'accordo con la risposta rimanente su un dato di fatto: quel "ritorno eccezionale" avviene e deve essere gestito.
Ma la ragione per cui esistono eccezioni non è che esistono rendimenti eccezionali. È possibile avere "valori di ritorno" per ciascuno dei possibili stati di errore, proprio come fa C. Il motivo per cui le eccezioni sono preferibili alla gestione degli errori di tipo C è che è estremamente raro che l'errore di una funzione venga gestito nel chiamante immediato. È molto più comune che lo stato di errore faccia esplodere lo stack fino a quando non viene posizionato dove può essere ragionevolmente gestito. Inoltre, un singolo codice di "gestione" gestirà chiamate di metodi differenti.
Se si utilizza la gestione degli errori di tipo C, è necessario creare una quantità estesa di codice per eseguire il bubbling e l'aggregazione degli errori fino al codice del gestore. Ciò richiede disciplina e rigore e complica drasticamente i valori di ritorno dai metodi, in cui ogni possibile rendimento eccezionale deve avere il proprio valore). Le eccezioni e le infrastrutture intorno a loro semplicemente fanno tutto questo per te. E mentre sono d'accordo, anche la gestione delle eccezioni ha bisogno di un certo livello di disciplina e rigore, ne ha bisogno molto meno.
Ricorda per prima cosa stai risolvendo i problemi. Immagina una soluzione semplice, aggiungendo due numeri. Dovresti fare quanto segue.
Questo è l'approccio che prendo sempre. È molto TDD ma funziona per me e devo dire che lo trovo più veloce di qualsiasi altro approccio. Le eccezioni sono pulite. E in un linguaggio come Python non ci sarebbe la codifica necessaria per aumentarne la maggior parte .. 'a' + 2 solleva un'eccezione gratis: -)
Trovo che le eccezioni siano più pulite per lo stile del codice dato che posso avvolgere il codice e gestire eventuali errori in una posizione specifica (il blocco catch), anziché copiare il mio codice con un sacco di istruzioni if ( if (result == -1)
), per controlla le condizioni di errore.
Le eccezioni sono, in parte, un riconoscimento che la gestione degli errori è difficile e mentre possiamo recuperare da eccezioni, è molto più facile da non .
Le eccezioni sono il modo in cui il programmatore dice "Ho questa situazione ideale che deve essere abbinata e non mi preoccuperò di spendere più linee né le situazioni di gestione del tempo che non lo sono. " In sostanza, quando si esegue un tipo di gestione delle eccezioni "catch-all", si è rinunciare :
} catch(e) {
print("Don't even know what error it is but **there's an error**. Exit / Retry.");
}
(Oltre alle eccezioni "catch-all", c'è anche lo stile longform "one-by-one" che è l' eccezione piuttosto che la norma ma non quello che la gente dice quando dice "gestione delle eccezioni".)
Per la lettura aggiuntiva, vedi link
Leggi altre domande sui tag exception-handling io