Come devo gestire l'eccezione che * dovrebbe * non essere mai lanciata? [duplicare]

9

Qual è il modo migliore per gestire gli errori che non dovrebbero mai accadere?

Il mio attuale modo di fare ciò è di lanciare un'eccezione se la "cosa che non dovrebbe accadere" è , in questo modo:

/*
 * Restoring from a saved state. This shouldn't be 
 * null unless someone in the future doesn't set it properly, in which 
 * case they will realize they did something wrong because it may crash.
 */
Object foo = bundle.getSerializable("foo");
if (foo != null) {
    doSomethingWith(foo);
} else {
    // This should never happen.
    if (BuildConfig.DEBUG) {
        throw new RuntimeException(
            "Foo is null!");
    }
    foo = new Object();
    doSomethingWith(foo);
}

Questo è un po 'inutile, perché questo non dovrebbe mai accadere . Ma nel caso in cui il codice si rovini e venga rilasciato in produzione con foo come null, dovrei lasciare questi tipi di controlli nel codice?

    
posta elimirks 29.04.2014 - 15:36
fonte

5 risposte

16

È incredibile la frequenza con cui le cose che non dovrebbero mai accadere.

Dovresti lanciare un RuntimeException con un messaggio di errore descrittivo e lasciare che l'applicazione si arresti. Se ciò dovesse accadere, allora sai che qualcosa è profondamente sbagliato.

    
risposta data 29.04.2014 - 16:02
fonte
8

È meglio non gestire un'eccezione se non riesci a gestirla in modo intelligente.

Questo dà l'opportunità di qualcosa in precedenza nello stack per gestire l'eccezione, poiché probabilmente ha più contesto di ciò che sta tentando di ottenere.

In alternativa, se sai che questo non dovrebbe mai accadere, questa è un'asserzione e probabilmente trarrebbe vantaggio dall'avere un'istruzione assert .

Object foo = bundle.getSerializable("foo");

assert foo != null : "Foo was null"

doSomethingWith(foo);

Per riattivare il mio commento, se la tua applicazione si trova in uno stato indeterminato (causato da un flusso di logica che non intendevi) è meglio uccidere l'applicazione il prima possibile per prevenire la corruzione o risultati non desiderati.

    
risposta data 29.04.2014 - 15:52
fonte
3

Perché gestisci un errore che non si verifica mai?

La programmazione informatica si occupa di assoluti. Non esiste una cosa come dovrebbe , potrebbe e forse . O l'errore accadrà o non lo farà. L'aggiunta di codice di gestione degli errori per qualcosa che ha una probabilità dello 0% di verificarsi è l'odore del codice. Il buon codice gestisce il cambiamento dello 0,01% quando si verifica.

Un buon modo di pensarlo è come questo.

If you only add error handling for 99% of the possible problems, then there only needs to be 100 problems for the source code to have an unhandled problem.

Il codice sorgente Overtime crescerà per far fronte a migliaia di problemi ...

Non mescolare la gestione degli errori con la logica aziendale

Un problema che vedo sempre è il codice sorgente che è stato sparato con la gestione degli errori. Soprattutto in Java, dove la gestione delle eccezioni è imposta dall'errore del compilatore.

Rende difficile leggere quale sia l'intento di una funzione quando si dirama più volte per gestire tutti i diversi casi di errore.

Al contrario, separa la gestione degli errori in funzioni specializzate. Mantieni la funzione originale focalizzata su un singolo intento.

Ad esempio;

public Object getSerializableSafely(Bundle bundle, String name)
{
     Object foo = bundle.getSerializable(name);
     if (foo != null)
     {
        return foo;
     }
     else if (BuildConfig.DEBUG) {
        throw new RuntimeException("Foo is null!");
     }
     return null;
}

public boolean Worker()
{
    Object foo = getSerializableSafely(bundle, "foo");
    if (foo == null)
    {
        return false;
    }
    // do work with foo
    return true;
}

Ora, non c'è niente di sbagliato nel lanciare un'eccezione di runtime nei build di debug. Possono essere grandi risparmiatori di tempo perché portano il debugger giusto dove si trova il problema.

Il problema è cercare di creare funzioni come Worker e aspettarsi che non falliscano mai. Segnalare vero / falso come risultato di un'operazione è molto meglio che avvolgere tutto in try / catch.

Se stavi utilizzando un'API di analisi XML. Vorresti che generasse un'eccezione se la alimentavi con XML non valido o vuoi che la funzione di analisi restituisca false? Mantieni la gestione delle eccezioni in relazione a casi che sono eccezionali e utilizza i risultati restituiti per comunicare il risultato di una funzione.

    
risposta data 29.04.2014 - 16:52
fonte
2

Quello su cui inciampo sempre è "Eccezione codifica non supportata" quando cambio una stringa in o da byte UTF-8. Quando UTF-8 sarà non supportato!?

Stai attento, però ... ci sono due livelli di "non dovrebbe mai accadere". C'è il livello in cui UTF-8 non è supportato. Poi c'è il "non dovrebbe mai accadere" quando lo sviluppatore sta guardando oltre la spalla del tester, sbattendo le palpebre e borbottando "che non dovrebbe mai accadere".

  • Quello che faccio di solito per il primo livello è il rollover di quella logica in una classe separata che fa ciò che hai fatto nel tuo esempio ... prendilo e convertilo in un RuntimeException. In questo modo il blocco try ... catch sgradevole e irrilevante è nascosto e testato separatamente dal resto del codice.

  • Per il secondo caso, " dovrebbe uno sviluppatore mai accadere", quelle sono davvero delle eccezioni, nel significato proprio della parola "eccezione". Ma è più pulito trattarli come condizioni di guardia, perché se si verifica quel caso, tutte le puntate sono disattivate e il codice non può completare il suo use case.

    Object foo = bundle.getSerializable("foo");
    
    // Guard: This should never happen.
    if (foo == null) {
        throw new RuntimeException("Foo is null!");
    }
    
    foo.doSomething();
    
  • La regola è "fallire velocemente", quindi il momento in cui rileviamo che le nostre ipotesi sono errate, il codice deve fermarsi. Nota che un esempio di puntatore nullo è un esempio di "fail fast" perché il codice si fermerà non appena proverai a deferenziare il valore, ma è uno di quegli errori che è difficile da definire.

Quindi, in questo caso, istanziare un foo vuoto e andare avanti è una violazione di "fail fast", quindi non farlo.

  • È anche corretto fare affidamento sul contratto client-server, in modo che ogni chiamata effettuata non debba essere seguita da un controllo nullo. Finché si effettuano chiamate interne (e non si convalidano dati provenienti dall'esterno), non è necessario convalidare ogni assunzione nel contratto client-server. Se una funzione afferma che restituirà un valore non nullo, quindi fidati di esso. Se fallisce, correggi la funzione, non il client.

  • Per le eccezioni del puntatore nullo in particolare, Guava ha una classe facoltativa molto carina che include un tipo e si assicura che il valore sia non nullo o non presente.

risposta data 29.04.2014 - 16:17
fonte
0

Il lancio di un'eccezione è costoso e può essere piuttosto scioccante per l'utente in base al sistema operativo e alla piattaforma. Inoltre, qui c'è un errore di blocco che è facile da verificare, vale a dire un valore nullo. Invece di lanciare un'eccezione, dovresti terminare con grazia, mostrando un messaggio all'utente e Closing () e Disposing () di tutte le risorse che richiedono tali azioni.

    
risposta data 29.04.2014 - 17:08
fonte

Leggi altre domande sui tag