Un buon uso dei blocchi di prova?

15

Mi trovo sempre a lottare con questo ... cercando di trovare il giusto equilibrio tra try / catching e il codice non sta diventando questo osceno pasticcio di schede, parentesi ed eccezioni che vengono rimessi nello stack delle chiamate come una patata bollente. Ad esempio, ho un'applicazione che sto sviluppando proprio ora che usa SQLite. Ho un'interfaccia Database che astrae le chiamate SQLite e un modello che accetta le cose per entrare / uscire dal Database ... Quindi, se / quando si verifica un'eccezione SQLite, deve essere sballottata sul Modello (chi l'ha chiamata ), che deve passarlo a chiunque abbia chiamato AddRecord / DeleteRecord / qualunque ...

Sono un fan delle eccezioni piuttosto che dei codici di errore restituiti perché i codici di errore possono essere ignorati, dimenticati, ecc., mentre un'eccezione deve essere gestita (garantito, I potrebbe catturare e andare avanti immediatamente ...) Sono certo che ci sarà un modo migliore di quello che sto facendo adesso.

Modifica: avrei dovuto formularlo in modo leggermente diverso. Capisco di ri-lanciare come tipi diversi e simili, l'ho scritto male e questa è colpa mia. La mia domanda è ... come si fa a mantenere il codice pulito quando lo fa? Inizia a sentirmi estremamente disordinato dopo un po '.

    
posta trycatch 01.04.2011 - 00:27
fonte

4 risposte

13

Pensaci in termini di strong tipizzazione, anche se non stai usando un linguaggio strongmente tipizzato - se il tuo metodo non può restituire il tipo che ti aspettavi, dovrebbe essere lanciato un fa eccezione.

Inoltre, anziché lanciare l'SQLException fino al modello (o, peggio, all'interfaccia utente), ogni livello dovrebbe catturare le eccezioni note e avvolgere / mutare / sostituirle con eccezioni adatte a quel livello:

Layer      Handles Exception
----------------------------
UI         DataNotFoundException
Model      DatabaseRetrievalException
DAO        SQLException

Questo dovrebbe aiutare a limitare il numero di eccezioni che stai cercando in ogni livello e aiutarti a mantenere un sistema di eccezioni organizzato.

    
risposta data 01.04.2011 - 00:39
fonte
9

Le eccezioni consentono di scrivere codice più pulito perché la maggior parte di esso si occupa del caso normale e i casi eccezionali possono essere gestiti in seguito, anche in un contesto diverso.

La regola per gestire le eccezioni (catching) è che deve essere eseguita dal contesto che può effettivamente fare qualcosa al riguardo. Ma quello ha un'eccezione:

Le eccezioni devono essere catturate ai limiti del modulo (specialmente i limiti dei livelli) e anche solo per racchiuderle e lanciare un'eccezione di livello superiore che ha significato per il chiamante. Ogni modulo e livello deve nascondere la sua implementazione dettagli anche riguardo alle eccezioni (un modulo heap può lanciare HeapFull ma mai ArrayIndexOutOfBounds).

Nel tuo esempio, è improbabile che i livelli superiori possano fare qualcosa su un'eccezione SQLite (se lo fanno, allora è tutto così abbinato a SQLite che non sarai in grado di passare il livello dati a qualcos'altro). Ci sono una serie di motivi prevedibili per cose come Add / Delete / Update per fallire, e alcuni di essi (cambiamenti incompatibili nelle transazioni concorrenti) sono impossibili da recuperare anche nel livello data / persistence (violazione delle regole di integrità, f.i.). Lo strato di persistenza dovrebbe tradurre le eccezioni in qualcosa di significativo nei termini del livello del modello, in modo che i livelli superiori possano decidere se riprovare o fallire con garbo.

    
risposta data 01.04.2011 - 00:40
fonte
5

Come regola generale dovresti prendere solo eccezioni specifiche (ad esempio IOException) e solo se hai qualcosa di specifico da fare una volta che hai rilevato l'eccezione.

Altrimenti, è spesso meglio lasciare che Eccezioni risalano in superficie in modo che possano essere esposte e gestite. Alcuni lo chiamano fail-fast.

Dovresti avere un qualche tipo di gestore alla radice della tua applicazione per catturare le Eccezioni non gestite che sono esplose da sotto. Ciò ti offre la possibilità di presentare, segnalare o gestire l'Eccezione in modo adeguato.

Wrapping Exceptions è utile quando devi lanciare un'eccezione su un sistema distribuito e il client non ha la definizione dell'errore sul lato server.

    
risposta data 01.04.2011 - 00:38
fonte
3

Immagina di scrivere una classe di stack. Non inserisci alcun codice di gestione delle eccezioni nella classe, in quanto potrebbe produrre le seguenti eccezioni.

  1. ArrayIndexError - generato quando l'utente tenta di eseguire il pop dallo stack vuoto
  2. NullPtrException - generato a causa di un bug nell'implementazione che causa un tentativo di fare riferimento a un riferimento null

Un approccio semplicistico al wrapping delle eccezioni potrebbe decidere di racchiudere entrambe queste eccezioni in una classe di eccezioni StackError. Tuttavia, questo manca davvero il punto di avvolgere le eccezioni. Se un oggetto genera un'eccezione di basso livello che dovrebbe significare che l'oggetto è rotto. Tuttavia, c'è un caso in cui questo è accettabile: quando l'oggetto è in realtà rotto.

Il punto di avvolgimento delle eccezioni è che l'oggetto dovrebbe dare le dovute eccezioni per gli errori normali. Lo stack deve sollevare StackEmpty non ArrayIndexError quando esce da una pila vuota. L'intento è non di evitare di generare altre eccezioni se l'oggetto o il codice sono interrotti.

Ciò che realmente vogliamo evitare è catturare eccezioni di basso livello che sono state passate attraverso oggetti di alto livello. Una classe di stack che lancia un ArrayIndexError quando esce da una pila vuota è un problema minore. Se in realtà si cattura ArrayIndexError, si presenta un problema serio. La propagazione degli errori di basso livello è un peccato molto meno grave che li cattura.

Per riportare questo al tuo esempio di SQLException: perché ricevi SQLExceptions? Una ragione è perché stai passando query non valide. Tuttavia, se il livello di accesso ai dati genera query non valide, è danneggiato. Non dovrebbe tentare di riavvolgere la sua rottura in un'eccezione DataAccessFailure.

Tuttavia, una SQLException potrebbe anche sorgere a causa di una perdita di connessione al database. La mia strategia su questo punto è quella di individuare l'eccezione nell'ultima riga di difesa, segnalare all'utente che la connettività del database è stata persa e arrestata. Dal momento che l'applicazione ha accesso alla perdita del database, non c'è davvero molto altro da fare.

Non so come sia il tuo codice. Ma sembra che potresti tradurre ciecamente tutte le eccezioni in eccezioni di livello superiore. Dovresti farlo solo in un numero relativamente piccolo di casi. La maggior parte delle eccezioni di livello inferiore indicano bug nel codice. La cattura e il riavvolgimento è controproducente.

    
risposta data 01.04.2011 - 03:53
fonte

Leggi altre domande sui tag