Gestione degli errori - In caso di errore di un programma in caso di errori o di ignorarli silenziosamente


20

Sto scrivendo un semplice programma per trasmettere MIDI su una rete. So che il programma incontrerà problemi di trasmissione e / o altre situazioni di eccezione che non sarò in grado di prevedere.

Per la gestione delle eccezioni, vedo due approcci. Devo scrivere il programma in modo che:

  • fallisce con il botto quando qualcosa va storto o
  • dovrebbe semplicemente ignorare l'errore e continuare, a scapito dell'integrità dei dati?

Quale approccio si aspetterebbe ragionevolmente un utente?
Esiste un modo migliore di gestire le eccezioni?

Inoltre, la mia decisione sulla gestione delle eccezioni dovrebbe essere influenzata dal fatto che io abbia a che fare o meno con una connessione di rete (ovvero qualcosa in cui posso ragionevolmente aspettarmi di avere problemi)?


1
@ GlenH7 Pagalo in avanti ... :)
moscerino

C'è un modo per fare +1 sulle modifiche? ;) Oh, aspetta, ti seguo. Certo, lo farà :)
Arlen Beiler

2
"Quale approccio un utente dovrebbe ragionevolmente aspettarsi?". Hai provato a chiedere a uno dei tuoi utenti? I test di usabilità in corridoio possono essere straordinariamente efficaci. Vedi blog.openhallway.com/?p=146
Bryan Oakley

Non ho utenti :)
Arlen Beiler

Risposte:


34

Mai e poi mai dovresti ignorare un errore riscontrato dal tuo programma. Come minimo, è necessario registrarlo in un file o in qualche altro meccanismo per la notifica. Ci possono essere situazioni occasionali in cui vorrai ignorare un errore ma documentarlo! Non scrivere un catchblocco vuoto senza commenti che spiegano perché è vuoto.

Se il programma dovrebbe fallire o meno dipende molto dal contesto. Se riesci a gestire l'errore con garbo, provaci. Se si tratta di un errore imprevisto, il programma si arresterà in modo anomalo. È praticamente la base della gestione delle eccezioni.


18
Non abbastanza per giustificare un -1, ma "mai" è una parola forte e ci sono situazioni in cui ignorare gli errori è la giusta linea di azione. Ad esempio, software per decodificare trasmissioni HDTV broadcast. Quando si verificano alcuni errori, che spesso si verificano, tutto ciò che si può fare è ignorarlo e continuare a decodificare ciò che verrà dopo.
whatsisname

1
@whatsisname: true, ma dovresti documentare chiaramente perché lo stai ignorando. Risposta aggiornata per considerare il tuo commento.
marco-fiset,

1
@ marco-fiset Non sono d'accordo. Arrestare l'intero programma è solo la soluzione se si tratta di una situazione in cui il riavvio risolverà il problema. Questo non è sempre il caso, quindi crash è spesso inutile e il default è semplicemente stupido. Perché vorresti sospendere l'intera operazione a causa di un errore localizzato? Non ha senso. La maggior parte delle applicazioni lo fa e, per qualche ragione, i programmatori stanno bene.
MaiaVictor

@Dokkat: il punto è non continuare con valori errati, il che darà una soluzione sbagliata (immondizia, immondizia in uscita). L'
arresto anomalo

1
@whatsisname Penso che potresti voler pensare alla tua definizione di "errore" in quel caso. Gli errori nei dati ricevuti non sono la stessa cosa degli errori nella struttura e nell'esecuzione di un programma software.
joshin4colours

18

Non dovresti mai ignorare silenziosamente gli errori, perché il tuo programma è basato su una serie di azioni che implicitamente dipendono da tutto ciò che è andato prima che vadano bene. Se qualcosa va storto nel passaggio 3 e si tenta di continuare con il passaggio 4, il passaggio 4 inizierà sulla base di ipotesi non valide, il che rende più probabile che finisca per generare anche un errore. (E se lo ignori anche tu, il passaggio 5 genera un errore e le cose iniziano a nevicare da lì.)

Il fatto è che, man mano che gli errori si accumulano, alla fine ti imbatterai in un errore così grande che non puoi ignorarlo, perché consisterà in qualcosa che viene dato all'utente e che qualcosa sarà completamente sbagliato. Quindi hai utenti che si lamentano con te per il fatto che il tuo programma non funziona correttamente e devi risolverlo. E se la parte "Dai qualcosa all'utente" è nel passaggio 28 e non hai idea che l'errore originale che sta causando tutto questo casino sia stato nel passaggio 3 perché hai ignorato l'errore nel passaggio 3, avrai un diamine di tempo il debug del problema!

D'altra parte, se quell'errore nel passaggio 3 fa esplodere tutto in faccia all'utente e genera un errore dicendo SOMETHING WENT BADLY WRONG IN STEP 3!(o il suo equivalente tecnico, una traccia dello stack), allora il risultato è lo stesso - l'utente che si lamenta di te il programma non funziona correttamente, ma questa volta sai esattamente dove iniziare a cercare quando vai a ripararlo .

MODIFICARE: in risposta ai commenti, se qualcosa va storto e previsto come previsto, è diverso. Ad esempio, in caso di ricezione di un messaggio non valido, non si tratta di un errore del programma; questo è "l'utente ha fornito un input errato che non ha convalidato la convalida". La risposta appropriata è quella di dire all'utente che ti sta dando un input non valido, che è quello che sembra che stai facendo. Non è necessario arrestarsi in modo anomalo e generare una traccia dello stack in un caso del genere.


Che ne dici di tornare all'ultima buona posizione conosciuta, o nel caso di un ciclo di ricezione, per semplicemente rilasciare quel messaggio e passare al successivo.
Arlen Beiler,

@Arlen: Anche quella può essere una cattiva idea. Si verificano errori perché è accaduto qualcosa che non avevi previsto durante la codifica . Ciò significa che tutti i tuoi presupposti sono usciti dalla finestra. Quella "ultima buona posizione conosciuta" potrebbe non essere più valida se si fosse a metà strada attraverso la modifica di una struttura di dati e ora si trova in uno stato incoerente, ad esempio.
Mason Wheeler,

E se me lo aspettassi, come messaggi corrotti che impediscono la deserializzazione. In alcuni casi, ho serializzato l'eccezione e l'ho rispedita attraverso il cavo. Ecco, vedi la mia modifica alla domanda.
Arlen Beiler,

@Arlen: se l'hai anticipato e sei sicuro di sapere quali sono gli effetti dell'errore e che sono contenuti correttamente, allora è diverso. Mi sembra che tu stia gestendo l'errore e rispondendo in modo appropriato, senza ignorarlo. Stavo parlando di ignorare errori imprevisti , ed è quello che sembrava che stavi chiedendo.
Mason Wheeler,

16

Esistono altre opzioni tra "esplosione" e "ignora".

Se l'errore è prevedibile ed evitabile, modificare il progetto o rifattorizzare il codice per evitarlo.

Se l'errore è prevedibile ma non evitabile, ma sai cosa fare quando si verifica, rileva l'errore e gestisci la situazione. Ma fai attenzione a non usare le eccezioni come controllo del flusso. E potresti voler registrare un avviso a questo punto e forse avvisare l'utente se ci sono delle azioni che potrebbero intraprendere per evitare questa situazione in futuro.

Se l'errore è prevedibile, inevitabile, e quando accade non c'è nulla che tu possa fare per garantire l'integrità dei dati, devi registrare l'errore e ricadere in uno stato sicuro (che, come altri hanno già detto, può significare crash).

Se l'errore non è qualcosa che ti sei aspettato, non puoi davvero essere sicuro di poter tornare a uno stato sicuro, quindi potrebbe essere meglio semplicemente accedere e bloccarsi.

Come regola generale, non cogliere alcuna eccezione di cui non puoi fare nulla, a meno che tu non preveda di registrarlo e ripeterlo. E nei rari casi in cui è inevitabile un try-catch-ignore, almeno aggiungi un commento nel blocco catch per spiegare il perché.

Vedi l'eccellente articolo sulla gestione delle eccezioni di Eric Lippert per ulteriori suggerimenti sulla categorizzazione e la gestione delle eccezioni.


1
La migliore risposta finora, secondo me.
giovedì

6

Queste sono le mie opinioni sulla domanda:

Un buon principio di partenza è fallire rapidamente. In particolare, non si dovrebbe mai scrivere un codice di gestione degli errori per qualsiasi errore per il quale non si conosce la causa esatta.

Dopo aver applicato questo principio, è possibile aggiungere un codice di ripristino per condizioni di errore specifiche che si verificano. È inoltre possibile introdurre diversi "stati sicuri" a cui tornare. L'interruzione di un programma è per lo più sicura, ma a volte potresti voler tornare a un altro stato buono conosciuto. Un esempio è come un sistema operativo moderno gestisce un programma offensivo. Spegne solo il programma, non l'intero sistema operativo.

Fallendo rapidamente e lentamente coprendo condizioni di errore sempre più specifiche, non si compromette mai l'integrità dei dati e si passa costantemente a un programma più stabile.

La deglutizione degli errori, ovvero il tentativo di pianificare errori per i quali non si conosce la causa esatta e quindi non si dispone di una strategia di recupero specifica, porta solo a una quantità crescente di codice per saltare gli errori e aggirare il programma. Dal momento che non ci si può fidare che i dati precedenti siano stati elaborati correttamente, inizierai a vedere i controlli sparsi per i dati errati o mancanti. La tua complessità ciclomatica sfuggirà di mano e ti ritroverai con una grande palla di fango.

Che tu sia a conoscenza o meno dei casi di fallimento è meno importante. Ma se ad esempio hai a che fare con una connessione di rete per la quale conosci una certa quantità di stati di errore, rimanda l'aggiunta della gestione degli errori fino a quando non aggiungi anche il codice di recupero. Ciò è in linea con i principi di cui sopra.


6

Non dovresti mai ignorare silenziosamente gli errori. E soprattutto non a scapito dell'integrità dei dati .

Il programma sta cercando di fare qualcosa. Se fallisce, devi affrontare il fatto e fare qualcosa al riguardo. Che cosa sarà qualcosa dipende da molte cose.

Alla fine l'utente ha richiesto al programma di fare qualcosa e il programma dovrebbe dire loro che non è riuscito. Ci sono molti modi in cui può farlo. Potrebbe interrompersi immediatamente, potrebbe persino ripristinare i passaggi già completati o, d'altra parte, può continuare e completare tutti i passaggi che può e di dire all'utente che questi passaggi hanno avuto esito positivo e che gli altri falliscono.

Il modo in cui scegli dipende da quanto strettamente sono collegati i passaggi e se è probabile che l'errore si ripeta per tutti i passaggi futuri, che a loro volta potrebbero dipendere dall'errore esatto. Se è richiesta una solida integrità dei dati, è necessario eseguire il rollback all'ultimo stato coerente. Se stai solo copiando un mucchio di file, puoi saltarne alcuni e dire alla fine all'utente che quei file non possono essere copiati. Non devi saltare silenziosamente i file e non dire nulla all'utente.

Modifica annuncio, l'unica differenza che fa è che dovresti provare a riprovare un certo numero di volte prima di rinunciare e dire all'utente che non ha funzionato, poiché è probabile che la rete abbia errori transitori che non si ripresenteranno se riprovi.


Intendevi davvero mettere un punto interrogativo alla fine della prima riga?
un CVn del

@ MichaelKjörling: No. Errore copia-incolla (copiato la formulazione dalla domanda e incluso il punto interrogativo per errore).
Jan Hudec,

4

Esiste una classe di casi in cui ignorare gli errori è la cosa giusta da fare: quando non c'è nulla che possa essere fatto sulla situazione e quando risultati scadenti e forse errati sono migliori di nessun risultato.

Il caso della decodifica del flusso HDMI a scopo di visualizzazione è un caso del genere. Se lo stream è male è male, urlare a riguardo non lo risolverà magicamente. Fai il possibile per visualizzarlo e lasciare che lo spettatore decida se è tollerabile o meno.


1

Non credo che un programma dovrebbe ignorare silenziosamente o causare il caos ogni volta che si verifica un problema.

Cosa faccio con il software interno che scrivo per la mia azienda ...

Dipende dall'errore, diciamo se è una funzione critica che sta inserendo dati in MySQL, deve far sapere all'utente che non è riuscito. Il gestore degli errori dovrebbe tentare di raccogliere quante più informazioni e fornire all'utente un'idea di come correggere l'errore da soli in modo che possano salvare i dati. Mi piace anche fornire un modo per inviarci silenziosamente le informazioni che cercavano di salvare, quindi se il peggio peggiora possiamo inserirle manualmente dopo che il bug è stato corretto.

Se non è una funzione critica, qualcosa che può sbagliare e non influire sul risultato finale di ciò che stanno cercando di ottenere, potrei non mostrare loro un messaggio di errore, ma posso inviargli un'e-mail che lo inserisca automaticamente nel nostro software di tracciamento dei bug o un gruppo di distribuzione e-mail che avvisa tutti i programmatori dell'azienda in modo che siamo a conoscenza dell'errore, anche se l'utente non lo è. Questo ci consente di riparare il back-end mentre sul front-end nessuno sa cosa sta succedendo.

Una delle cose più grandi che cerco di evitare è il crash del programma dopo l'errore, non riuscire a recuperare. Cerco sempre di dare all'utente la possibilità di continuare senza chiudere l'applicazione.

Credo che se nessuno conoscesse il bug, non verrà mai risolto. Sono anche fermamente convinto della gestione degli errori che consente all'applicazione di continuare a funzionare una volta scoperto un bug.

Se l'errore è correlato alla rete, perché non è necessario che le funzioni eseguano un semplice test di comunicazione di rete prima di eseguire la funzione per evitare l'errore? Quindi avvisa l'utente che non è disponibile una connessione, verifica la tua connessione Internet ecc. Ecc. E riprova?


1
Personalmente eseguo molte verifiche prima di eseguire funzioni critiche per cercare di limitare gli errori che non capisco completamente e che non posso fornire una gestione degli errori utile che restituisce informazioni dettagliate. Non funziona il 100% delle volte, ma non ricordo l'ultima volta che ho avuto un bug che mi aveva fatto perdere per ore.
Jeff

1

La mia strategia è di distinguere tra errori di codifica (bug) ed errori di runtime e, per quanto possibile, rendere difficile la creazione di errori di codifica.

I bug devono essere corretti il ​​prima possibile, quindi un Design by Contract è appropriato un approccio . In C ++, mi piace controllare tutte le mie precondizioni (input) con asserzioni nella parte superiore della funzione al fine di rilevare il bug il più presto possibile e semplificare il collegamento di un debugger e correggere il bug. Se invece lo sviluppatore o il tester scelgono di provare a continuare a eseguire il programma, qualsiasi perdita di integrità dei dati diventa il loro problema.

E trova i modi per prevenire il bug in primo luogo. Essere rigorosi con la correttezza const e scegliere i tipi di dati appropriati per i dati che terranno sono due modi per rendere difficile la creazione di bug. Fail-Fast è anche buono al di fuori del codice critico per la sicurezza che ha bisogno di un modo per recuperare.

Per errori di runtime che potrebbero verificarsi con codice privo di bug, come errori di comunicazione seriale o di rete o file mancanti o corrotti:

  1. Registra l'errore.
  2. (Facoltativo) Tentare di riprovare in silenzio o altrimenti ripristinare l'operazione.
  3. Se l'operazione continua a fallire o è irrecuperabile, segnalare visibilmente l'errore all'utente. Quindi, come sopra, l'utente può decidere cosa fare. Ricorda il principio del minimo stupore , perché una perdita di integrità dei dati è sorprendente per l'utente a meno che tu non li abbia avvertiti in anticipo.

0

Fallire è l'opzione giusta quando hai motivi per pensare che lo stato generale del programma sia instabile e qualcosa di brutto può accadere se lo lasci funzionare da ora in poi. Un po '"ignorandolo" (cioè, come altri hanno sottolineato, registrandolo da qualche parte o visualizzando un messaggio di errore per l'utente, quindi proseguendo) è ok quando sai che, sicuramente, l'operazione corrente non può essere eseguita, ma il programma può continuare a correre.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.