Accesso
Vorrei iniziare con la registrazione. Beh, sono sicuro che lo sapete, ci sono vari livelli di registrazione che la maggior parte delle librerie supportano: Debug, Info, Warning ed Error.
Il mio approccio è che l'output di Debug dovrebbe mostrare tutte le informazioni che io, il programmatore, vorremmo vedere. L'utente che usa il tuo programma probabilmente non vorrà vedere che hai inserito un metodo specifico nella GUI, ma lo farei in caso di crash. Se non hai modo di recuperare la traccia dello stack del crash, la prossima cosa migliore è stampare sul log la voce e l'uscita di ogni metodo nel tuo programma con il debug a livello di log.
Nel tuo esempio, questo significa un log con il seguente output:
DEBUG <timestamp>: ClassA::A() : Entering A
DEBUG <timestamp>: ClassB::B() : Entering B
DEBUG <timestamp>: ClassC::C() : Entering C
DEBUG <timestamp>: ClassC::C() : Exiting C // These don't show
DEBUG <timestamp>: ClassB::B() : Exiting B // in the case of
DEBUG <timestamp>: ClassA::A() : Exiting A // an error in C.
Informazioni sul livello del registro sono informazioni che ritengo importanti per la funzionalità del programma. Supponiamo per gli scopi di questo esempio che l'obiettivo di A sia eseguire un'operazione utilizzando un database selezionato, B è un mezzo per visualizzare le connessioni di database esistenti e creare nuove connessioni di database e C è l'informazione specifica relativa a un nuovo database e un modo per testare questa connessione.
I messaggi di registro del livello informazioni pertinenti per me sarebbero:
INFO <timestamp>: ClassC::C() : Saved database connection "localhost".
INFO <timestamp>: ClassA::A() : Performing transaction using database connection "localhost".
INFO <timestamp>: ClassA::A() : Transaction complete using database connection "localhost".
Si noti che questi sono messaggi che probabilmente vorrai mostrare all'utente nel programma (anche se un po 'prolisso). Per me questa è la differenza tra Debug e Info: anche se l'utente potrebbe voler vedere queste informazioni. Ovviamente anche gli avvisi e gli errori di registrazione devono essere registrati e mostrati all'utente.
WARNING <timestamp>: ClassC::C() : Connection test for "localhost" failed.
Quello che faccio di solito nei miei programmi è un parametro facoltativo che determina a quale livello di registrazione dovrei presentare i messaggi all'utente con un valore predefinito di Info. Se l'utente non desidera un'interfaccia dettagliata, può impostarlo su Avviso e visualizzare solo avvisi ed errori e, ad esempio, è possibile utilizzarlo per generare informazioni di debug. In tal caso, assicurati di fornire un indicatore alternativo che un determinato compito è terminato (il pulsante di esecuzione diventa disabilitato durante l'elaborazione e successivamente viene nuovamente abilitato, ad esempio).
Eccezioni
Detto questo, come approccio le eccezioni? Dipende dal contesto dell'errore. Se ClassC gestisce la creazione di nuove connessioni al database e testando la connessione, genera un'eccezione, vorrei gestire quell'eccezione sql all'interno di ClassC poiché mi aspetto potenziali problemi. Si noti che gestirò solo un'eccezione sql. Se il problema fosse un altro che non mi aspettavo, scivolerebbe nella classe C e di conseguenza passerei a ClassB. ClassB può decidere di rilevare le eccezioni relative ai driver mancanti, poiché non è del tutto inatteso e non vorrei che tali problemi venissero rilevati a livelli inferiori.
Tuttavia, supponendo che non lo sia, colpiamo ClassA, il cui ruolo nel chiamare ClassB era consentire all'utente la possibilità di creare nuove connessioni al database. Se un'eccezione colpisce ClassA, è successo qualcosa di grave. In tali casi, ciò che faccio di solito è di rilevare qualsiasi eccezione, scrivere l'errore e il suo stacktrace (se possibile) nel log, mostrare un messaggio di errore imprevisto all'utente, quindi rilanciare l'eccezione. Secondo la mia opinione personale, è irresponsabile cogliere tutte le eccezioni senza essere pronti a ripensarla, dato che è corretto che gli errori imprevisti siano ancora generati.
Il punto importante da notare qui è il livello di responsabilità. Le eccezioni che mi aspetto sono gestite localmente mentre eccezioni inaspettate nel contesto della mia classe sono autorizzate a risalire la catena. Nel peggiore dei casi, assicurarsi di provare a registrare l'errore e mostrare un messaggio di errore all'utente. Combina questo con la registrazione di precisione e nella maggior parte dei casi sarai sicuro di riuscire a tracciare i tuoi passi fino al luogo dell'errore.