Eccezioni "Errore di programmazione" - Il mio approccio è valido?


9

Attualmente sto cercando di migliorare il mio uso delle eccezioni e ho trovato l'importante distinzione tra eccezioni che indicano errori di programmazione (ad esempio, qualcuno ha passato null come argomento o chiamato un metodo su un oggetto dopo che è stato eliminato) e quelli che indicano un errore nel operazione che non è colpa del chiamante (ad esempio un'eccezione I / O).

Come dovrebbero essere trattati in modo diverso questi due tipi di eccezioni? Ritieni che le eccezioni di errore debbano essere esplicitamente documentate o è sufficiente per documentare le condizioni preliminari pertinenti? E puoi tralasciare la documentazione di una precondizione o di un'eccezione di errore se dovrebbe essere ovvio (ad esempio, ObjectDisposedExceptionquando si chiama un metodo su un oggetto disposto)


Di solito distinguo un'altra categoria di eccezioni: le eccezioni commerciali. Si riferiscono a condizioni di errore di cui l'utente è interessato ai dettagli. In genere faccio verificare queste eccezioni.
Beluchin,

Risposte:


2

Penso che tu sia sulla buona strada. Né lanciare, catturare, né documentare tutte le eccezioni potenzialmente gettabili ha molto senso. Vi sono momenti in cui la rigorosità del prodotto richiede un livello più elevato di impiego e documentazione delle eccezioni (ad esempio alcuni aspetti critici per la sicurezza di un sistema).

La strategia di essere più difensiva, usando i concetti del contratto per identificare le precondizioni (e le postcondizioni) in particolare sui chiamanti a valle (ad esempio qualsiasi cosa che assomigli a un membro pubblico o protetto) sarà spesso più efficace e più flessibile. Questo vale non solo per l'implementazione, ma per la documentazione. Se gli sviluppatori sanno cosa ci si aspetta, hanno maggiori probabilità di seguire le regole e meno probabilità di essere confusi o di abusare del codice che hai scritto.

Alcune delle cose comuni che dovrebbero essere documentate includono il caso di parametri null. Spesso c'è una conseguenza per il loro uso che porta il risultato a qualcosa che normalmente non ci si aspetterebbe, ma che è consentito e utilizzato per una varietà di ragioni, a volte per flessibilità. Come consumatore di un membro che ha parametri che consentono valori nulli o altri valori speciali, non razionali (come tempo negativo o quantità negative), mi aspetto di vederli identificati e spiegati.

Per i parametri non nulli, in quanto consumatore di un membro pubblico o protetto, voglio sapere che null non è consentito. Voglio sapere qual è l'intervallo di valori valido in un determinato contesto. Voglio conoscere le conseguenze dell'utilizzo di valori che non rientrano nell'intervallo normale, ma che sono comunque validi in un diverso contesto di chiamata (ad esempio, il valore del tipo è generalmente valido per qualsiasi operazione, ma non qui - come un parametro booleano che non non aspettarsi false come valore valido.

Per quanto riguarda la piattaforma, o interfacce altrimenti ben note, non credo che si debba fare il possibile per documentarlo. Tuttavia, poiché hai la possibilità come sviluppatore di variare l'implementazione da qualsiasi orientamento della piattaforma, prendi nota di come ne consegue che l'orientamento può essere utile.

Specifico per IDisposable, spesso le implementazioni di questa interfaccia offrono un metodo alternativo che è preferito rispetto al processo di smaltimento esplicito. In questi casi, evidenziare il metodo preferito e notare che lo smaltimento esplicito non è preferito.


Comprendo la necessità di documentare quali valori sono consentiti, ma se vedi una funzione performReadCommand(ICommand cmd, int replySizeBytes), ti aspetteresti che null sia accettabile per cmd o un valore negativo per replySizeBytes? La documentazione IMO sarebbe necessaria solo se quei valori fossero effettivamente consentiti e probabilmente dovresti indicare se 0 è valido per replySizeBytes.
Medo42,

Entrambi potrebbero essere "validi" a seconda dell'implementazione. Tu o io, e anche tutti qui non si aspetterebbero che valori nulli o negativi siano appropriati per quella firma, ma potrebbero esserlo. Considerare un ambiente in cui il lettore è un processo persistente e bloccante e quando il cmd è nullo potrebbe significare non modificare il comando utilizzato dal lettore e procedere con la lettura successiva utilizzando il comando precedente. Idealmente, in quel caso sarebbe opportuno definire e utilizzare una firma del metodo più appropriata che omettesse il cmd come parametro, ma potrebbe non esserlo.
Giustino

2

Ecco i miei pensieri. Nota che non sono troppo sicuro che questo sia il modo migliore di procedere, motivo per cui ho creato questa domanda in primo luogo.

Per quanto ho capito, ha poco senso per un chiamante immediato gestire effettivamente le eccezioni di errore di programmazione, dovrebbe invece assicurare che siano soddisfatte le condizioni preliminari. Solo i gestori di eccezioni "esterni" ai limiti delle attività dovrebbero rilevarli, in modo che possano mantenere il sistema in esecuzione se un'attività non riesce.

Al fine di garantire che il codice client possa rilevare in modo chiaro le eccezioni di "errore" senza rilevare errori per errore, creo ora le mie classi di eccezione per tutte le eccezioni di errore e le documento nei metodi che le generano. Li farei controllare le eccezioni in Java.

Fino a poco tempo fa, ho cercato di documentare tutte le eccezioni che un metodo potrebbe generare, ma che a volte crea un elenco inconsapevole che deve essere documentato in ogni metodo nella catena di chiamate fino a quando non puoi dimostrare che l'errore non si verificherà. Invece, ora documento i presupposti nelle descrizioni di riepilogo / parametro e non menziono nemmeno cosa succede se non vengono rispettati. L'idea è che le persone non dovrebbero cercare di cogliere esplicitamente queste eccezioni, quindi non è necessario documentarne il tipo.

Per documentare i presupposti, affermare l'ovvio crea solo disordine inutile: se passare null a un metodo non ha alcun senso ovvio, il chiamante deve aspettarsi un'eccezione se passa comunque null, anche se non è documentato. Lo stesso vale per il caso di ObjectDisposedException: questa interfaccia è così ampiamente utilizzata che qualcuno che chiama Dispose sarebbe consapevole della responsabilità di garantire che nessuno continuerà a utilizzare l'oggetto.


1

Una regola empirica ragionevole:

  1. Cattura qualsiasi eccezione che puoi correggere.
  2. Cattura, commenta e rilancia qualsiasi eccezione che non puoi risolvere ma che può dire qualcosa di utile.
  3. Non cogliere alcuna eccezione che non è possibile elaborare o diagnosticare meglio dell'elaborazione predefinita.

Potresti voler avere un gestore di eccezioni non fatale di livello superiore per intercettare qualsiasi cosa che non possa essere gestita di seguito per cercare di impedire la caduta dell'applicazione, ma ciò dipenderà fortemente dalla tua particolare applicazione. Ad esempio, le applicazioni iOS preferiscono intrappolare il più possibile; un'app da riga di comando potrebbe essere perfettamente a posto se non intercetta quasi nessuna eccezione.


0

Persino Java non "documenta" tutte le eccezioni. Nonostante il requisito che ogni thrown eccezione venga menzionata in una throwsclausola, qualsiasi riga di codice può generare a RuntimeExceptionsenza la necessità di dichiararla nella firma del metodo.


Ciò è in contrasto con i consigli popolari, in particolare, Effective Java, 2nd ed, Item 62 è "Documenta tutte le eccezioni generate da ciascun metodo". In effetti, Bloch riconosce che le eccezioni non controllate sono generalmente per errori di programmazione, ma che dovrebbero anche essere documentate con attenzione, poiché questo è "il modo migliore" per documentare le condizioni preliminari di un metodo. Non sono sicuro però di essere d'accordo. Se documento le esecuzioni, le rendo parte del contratto di interfaccia e potrebbe essere necessario aggiungere codice per mantenerle invariate se la mia implementazione cambia.
Medo42,

1
Per ogni esperto, c'è un controesperto. Bruce Eckel, autore del piuttosto famoso Thinking in Java , una volta era nel campo delle eccezioni controllate dai professionisti e ha cambiato squadra. C'è una bella discussione tra Eckels e Anders Hejlsberg , il creatore di C #, che non ha verificato le eccezioni.
Ross Patterson,

0

Il modo standard per farlo è con l'approccio java. Gli errori di programmazione dovrebbero essere eccezioni non selezionate e non dovrebbero essere colti per garantire un errore rapido. Gli errori di contratto sono eccezioni verificate e devono essere gestiti in modo appropriato dal cliente.


2
Consideri (ad esempio) un NULL calcolato (o un valore di errore) come un errore di programmazione o un errore di contratto ? Vado con la tua distinzione, ma il confine non è così ben definito :)
Andrew

Se il null viene calcolato da un argomento passato (come un argomento null o qualcosa di non valido), si tratta di un errore di contratto. Se il valore nullo viene calcolato a causa di un codice errato, si tratta di un errore di programmazione. Se si suppone che il null sia calcolato, non si tratta di un errore. Non vedo davvero alcuna ambiguità in questo esempio.
J.Ashworth,
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.