Gestione delle eccezioni: quando e perché?

0

Le lingue principali che uso sono C ++ e Java.

Entrambe le lingue supportano la gestione delle eccezioni.

Confesso che forse non sono in grado di comprendere la gestione delle eccezioni, almeno, di certo non capisco perché ne avresti bisogno.

Voglio costruire questa domanda qui:

Programmazione difensiva vs gestione delle eccezioni

Il dibattito tra la gestione difensiva / delle eccezioni lo trovo molto interessante.

Nella mia esperienza, devo ancora incontrare una situazione che pensavo richiedesse una gestione delle eccezioni. Come ho affermato, potrei semplicemente non capire il concetto correttamente. Detto questo, sono al punto in cui penso che, se finisci nella situazione in cui hai bisogno di una gestione delle eccezioni, il codice è strutturato in modo improprio.

Abbastanza un reclamo, senza dubbio, dal momento che molte librerie professionali usano eccezioni e sono sicuramente scritte da persone che superano le mie capacità e provano molte volte.

La mia domanda è: qualcuno ha riscontrato un problema in cui la gestione delle eccezioni era la migliore soluzione possibile?

In tal caso, spiegate in dettaglio il problema e perché la gestione delle eccezioni è stata la soluzione migliore.

    
posta bigcodeszzer 05.04.2016 - 01:02
fonte

2 risposte

8

The debate between defensive/exception handling I find very interesting.

Sembra che tu stia insinuando che questi due concetti sono in disaccordo l'uno con l'altro. Non lo sono. Di fatto, una forma comune di programmazione difensiva sta controllando gli argomenti di una funzione e generando eccezioni se non sono validi.

But at the same time, if that happens I would prefer if the compiler /stops/ the program and says, "You raised an error." So I can fix it.

Uno dei vantaggi del lancio di eccezioni è che per impostazione predefinita interromperanno il programma.

Un confronto più utile sarebbe costituito dai codici di errore rispetto alle eccezioni. Questi sono entrambi i meccanismi per affrontare i fallimenti inevitabili. In altre parole, i guasti che non sono il risultato di un errore di programmazione, ma sono qualcosa che il programmatore semplicemente non può impedire. Esempi comuni includono l'esaurimento della memoria, un determinato file non esistente o che non consente l'accesso in scrittura o un timeout della richiesta di rete.

Prima delle eccezioni, questi tipi di errori erano solitamente indicati da un codice di errore restituito dalla funzione che potrebbe inevitabilmente fallire. I problemi con questo approccio sono: 1) È facile per un programmatore semplicemente non controllare mai i codici di errore, lasciando una lista quasi infinita di bug rari e sottili con conseguenze completamente imprevedibili. 2) Poiché è solo un numero intero, non si ottengono molte informazioni sul perché si è verificato l'errore. 3) In molti casi, il luogo in cui viene chiamata la potente funzione di non funzionamento non è un luogo in cui un guasto può essere ragionevolmente recuperato. Ciò richiede che il codice di errore sia "passato di nuovo" allo stack di chiamate da ogni singola funzione che può chiamarlo direttamente o indirettamente. Inutile dire che questo è soggetto a errori.

Le eccezioni erano intese come soluzione a questi problemi. Quando viene lanciata un'eccezione, è impossibile per il programmatore ignorarla accidentalmente; o fanno di tutto per catturarlo e gestirlo, o il programma si blocca. Le eccezioni tendono anche a contenere molte più informazioni sull'errore rispetto a un singolo intero. E non è necessario scrivere alcun codice per consentire alle eccezioni di propagarsi attraverso una funzione; continuano a salire finché non raggiungono il codice che in realtà vuole gestirli.

Quindi i posti migliori per usare le eccezioni sono quando si ha un potenziale fallimento inevitabile che sarebbe pericoloso ignorare e in genere è difficile o impossibile da recuperare nel luogo in cui si verifica.

Come esempio semplice e forse un po 'estremo, considera ciò che accade quando esaurisci la memoria. Qualsiasi funzione che crea oggetti può potenzialmente esaurire la memoria. Se dovessi provare a gestire questi errori correttamente con i codici di errore, quasi ogni singola funzione non banale nel tuo programma dovrebbe ricordare di controllare se l'allocazione fallita, controllare se qualcuna delle altre funzioni ha fallito la sua chiamata e restituire l'uscita codice di memoria quando lo fanno. E non esiste un modo corretto per gestire il problema oltre a mostrare all'utente un messaggio di errore "Memoria insufficiente" (che è tipicamente possibile solo vicino alla parte superiore del programma). Quindi il fatto che C ++ gestisca questo tramite un'eccezione consente di risparmiare una quantità enorme di codice estremamente noioso e soggetto a errori; è sufficiente scrivere il codice per mostrare il messaggio di errore "Memoria insufficiente".

Talvolta le eccezioni sono utili anche per il rilevamento e la rapida interruzione degli errori logici. Supponi di scrivere una funzione radice quadrata. Funziona solo su numeri positivi, ma il compilatore non può impedire alle persone di passare un numero negativo. Puoi semplicemente ignorare questo problema e dichiarare che chiunque faccia questo errore sta invocando un comportamento indefinito (vedi "design by contract"). Ma la programmazione difensiva è spesso una buona cosa e vorremmo che la nostra API fosse un po 'più user-friendly di così. Non è possibile restituire un codice di errore poiché si prevede che la funzione radice quadrata restituisca una radice quadrata. È possibile registrare un messaggio di errore, ma questo non arresta immediatamente il programma in modo che il programmatore non possa notarlo. Se si lancia un'eccezione, ciò interromperà immediatamente il programma e visualizzerà un messaggio di errore chiaro, quindi di solito è una buona scelta. Molte lingue hanno anche una sorta di meccanismo di "asserzione" che è anche una buona soluzione per questo. Le asserzioni si comportano in modo molto simile alle eccezioni, tranne per il fatto che non puoi prenderle, quindi sono in qualche modo più semplici.

    
risposta data 05.04.2016 - 01:33
fonte
1

Ho una visione semplice di questo. Quando una funzione non può restituire un valore o un metodo non è in grado di soddisfare le sue post-condizioni, genera un'eccezione. Non importa se l'eccezione viene lanciata l'1% delle volte o il 15% delle volte o il 40% delle volte. Scrivi il codice più pulito possibile e preoccupati del costo di generare un'eccezione quando trovi un problema di prestazioni. È molto più facile scrivere e leggere:

retrieve data
calculate statistics
write results

di

retrieve data
got it?
   no, return error 1
calculate statistics
have the results?
   no, return error 2
write results
all ok?
   no, return error 3

La maggior parte delle volte non c'è nulla da fare su nessuna di queste condizioni e dovrebbero essere catturate e registrate al livello più alto.

La cosa peggiore da fare è riempire il codice con blocchi catch che non fanno altro che log e rethrow. Lascia la registrazione per il livello superiore. Sì, la traccia dello stack sarà più profonda. Nessun problema, la parte interessante è ancora in cima.

    
risposta data 05.04.2016 - 08:53
fonte

Leggi altre domande sui tag