Un metodo di recupero dovrebbe restituire "null" o generare un'eccezione quando non è in grado di produrre il valore restituito? [chiuso]

StackOverflow https://stackoverflow.com/questions/175532

  •  05-07-2019
  •  | 
  •  

Domanda

Ho un metodo che dovrebbe restituire un oggetto se viene trovato.

Se non viene trovato, dovrei:

  1. restituisce null
  2. genera un'eccezione
  3. altro
È stato utile?

Soluzione

Se ti aspetti sempre di trovare un valore, lancia l'eccezione se manca. L'eccezione significherebbe che c'era un problema.

Se il valore può essere mancante o presente ed entrambi sono validi per la logica dell'applicazione, restituire un valore null.

Più importante: cosa fai in altri punti del codice? La coerenza è importante.

Altri suggerimenti

Generare un'eccezione solo se è veramente un errore. Se si prevede che il comportamento dell'oggetto non esista, restituire il valore null.

Altrimenti è una questione di preferenza.

Come regola generale, se il metodo deve sempre restituire un oggetto, seguire l'eccezione. Se anticipi il null occasionale e vuoi gestirlo in un certo modo, vai con il null.

Qualunque cosa tu faccia, sconsiglio vivamente la terza opzione: restituire una stringa che dice "WTF".

Se null non indica mai un errore, restituire null.

Se null è sempre un errore, genera un'eccezione.

Se a volte null è un'eccezione, allora codifica due routine. Una routine genera un'eccezione e l'altra è una routine di test booleana che restituisce l'oggetto in un parametro di output e la routine restituisce un falso se l'oggetto non è stato trovato.

È difficile abusare di una routine Try. È davvero facile dimenticare di verificare la presenza di null.

Quindi quando null è un errore basta scrivere

object o = FindObject();

Quando il null non è un errore, puoi codificare qualcosa come

if (TryFindObject(out object o)
  // Do something with o
else
  // o was not found

Volevo solo ricapitolare le opzioni menzionate prima, gettandone alcune nuove in:

  1. restituisce null
  2. genera un'eccezione
  3. usa il modello di oggetti null
  4. fornisce un parametro booleano al tuo metodo, in modo che il chiamante possa scegliere se desidera che tu generi un'eccezione
  5. fornisce un parametro aggiuntivo, in modo che il chiamante possa impostare un valore che ottiene se non viene trovato alcun valore

Oppure puoi combinare queste opzioni:

Fornisci diverse versioni sovraccariche del tuo getter, in modo che il chiamante possa decidere da che parte andare. Nella maggior parte dei casi, solo il primo ha un'implementazione dell'algoritmo di ricerca e gli altri avvolgono semplicemente il primo:

Object findObjectOrNull(String key);
Object findObjectOrThrow(String key) throws SomeException;
Object findObjectOrCreate(String key, SomeClass dataNeededToCreateNewObject);
Object findObjectOrDefault(String key, Object defaultReturnValue);

Anche se scegli di fornire una sola implementazione, potresti voler usare una convenzione di denominazione come quella per chiarire il tuo contratto, e ti aiuta se dovessi decidere di aggiungere anche altre implementazioni.

Non dovresti usarlo eccessivamente, ma potrebbe essere utile, specialmente quando scrivi una classe di supporto che userai in centinaia di applicazioni diverse con molte convenzioni sulla gestione degli errori.

Usa il modello di oggetti null o genera un'eccezione.

Sii coerente con le API che stai utilizzando.

Chiediti: " è un caso eccezionale che l'oggetto non sia trovato " ;? Se si prevede che accada nel normale corso del programma, probabilmente non dovresti sollevare un'eccezione (poiché non si tratta di un comportamento eccezionale).

Versione breve: utilizzare le eccezioni per gestire comportamenti eccezionali, non per gestire il normale flusso di controllo nel programma.

-Alan.

dipende se la tua lingua e il tuo codice promuovono: LBYL (guarda prima di saltare) o EAFP (più facile chiedere perdono che autorizzazione)

LBYL dice che dovresti controllare i valori (quindi restituisci un valore nullo)
EAFP dice di provare l'operazione e vedere se fallisce (lancia un'eccezione)

anche se sono d'accordo con quanto sopra .. le eccezioni dovrebbero essere usate per condizioni eccezionali / di errore, e la restituzione di un valore null è la cosa migliore quando si usano i controlli.


EAFP vs. LBYL in Python:
http://mail.python.org/pipermail/python-list /2003-May/205182.html ( Archivio Web )

Vantaggi del lancio di un'eccezione:

  1. Flusso di controllo più pulito nel codice chiamante. Il controllo di null inietta un ramo condizionale che viene gestito in modo nativo da try / catch. Il controllo di null non indica ciò che stai cercando: stai controllando null perché stai cercando un errore che ti aspetti o stai controllando null in modo da non passare oltre su downchain ?
  2. Rimuove l'ambiguità di ciò che " null " significa. Null è rappresentativo di un errore o è null ciò che è effettivamente memorizzato nel valore? Difficile dirlo quando hai solo una cosa su cui basare quella determinazione.
  3. Miglioramento della coerenza tra il comportamento del metodo in un'applicazione. Le eccezioni sono in genere esposte nelle firme dei metodi, quindi sei più in grado di capire a quali casi limite sono associati i metodi in un'applicazione e quali informazioni l'applicazione può reagire in modo prevedibile.

Per ulteriori spiegazioni con esempi, consultare: http : //metatations.com/2011/11/17/returning-null-vs-throwing-an-exception/

Le eccezioni sono correlate alla progettazione per contratto.

L'interfaccia di un oggetto è in realtà un contratto tra due oggetti, il chiamante deve soddisfare il contratto oppure il ricevitore potrebbe non riuscire con un'eccezione. Esistono due possibili contratti

1) tutti gli input il metodo è valido, nel qual caso è necessario restituire null quando l'oggetto non viene trovato.

2) è valido solo un input, ovvero ciò che risulta in un oggetto trovato. Nel qual caso DEVI offrire un secondo metodo che consenta al chiamante di determinare se il suo input sarà corretto. Ad esempio

is_present(key)
find(key) throws Exception

SE e SOLO SE fornisci entrambi i metodi del 2o contratto, ti è consentito lanciare un'eccezione se non viene trovato nulla!

Preferisco solo restituire un valore null e fare affidamento sul chiamante per gestirlo in modo appropriato. L'eccezione (per mancanza di una parola migliore) è se sono assolutamente "certo" che questo metodo restituirà un oggetto. In tal caso, un fallimento è un evento eccezionale che deve essere lanciato.

Dipende da cosa significa che l'oggetto non è stato trovato.

Se è una situazione normale, restituisce null. Questo è solo qualcosa che potrebbe accadere di tanto in tanto e i chiamanti dovrebbero verificarlo.

Se si tratta di un errore, quindi genera un'eccezione, i chiamanti dovrebbero decidere cosa fare con la condizione di errore dell'oggetto mancante.

Alla fine entrambi funzionerebbero, sebbene la maggior parte delle persone generalmente consideri una buona pratica usare le Eccezioni solo quando si è verificato qualcosa di eccezionale.

Ecco qualche altro suggerimento.

Se si restituisce una raccolta, evitare di restituire null, restituire una raccolta vuota che semplifica la gestione dell'enumerazione senza prima un controllo null.

Diverse API .NET utilizzano il modello di un parametro thrownOnError che consente al chiamante di scegliere se si tratta davvero di una situazione eccezionale o meno se l'oggetto non viene trovato. Type.GetType è un esempio di questo. Un altro modello comune con BCL è il modello TryGet in cui viene restituito un valore booleano e il valore viene passato tramite un parametro di output.

In alcune circostanze potresti anche considerare il modello Null Object che può essere una versione predefinita o una versione senza comportamento. La chiave è evitare controlli null in tutta la base di codice. Vedi qui per maggiori informazioni http://geekswithblogs.net/dsellers/archive /2006/09/08/90656.aspx

In alcune funzioni aggiungo un parametro:

..., bool verify = true)

Vero significa lancio, falso significa restituire un valore di ritorno errore. In questo modo, chiunque utilizzi questa funzione ha entrambe le opzioni. L'impostazione predefinita dovrebbe essere vera, a vantaggio di coloro che dimenticano la gestione degli errori.

Restituisce un valore null anziché generare un'eccezione e documenta chiaramente la possibilità di un valore di ritorno null nella documentazione dell'API. Se il codice chiamante non rispetta l'API e controlla il caso nullo, molto probabilmente si tradurrà in una sorta di eccezione "puntatore null" comunque :)

In C ++, posso pensare a 3 diversi tipi di impostazione di un metodo che trova un oggetto.

Opzione A

Object *findObject(Key &key);

Restituisce null quando non è possibile trovare un oggetto. Bello e semplice. Vorrei andare con questo. Gli approcci alternativi di seguito sono per le persone che non odiano i out-params

Opzione B

void findObject(Key &key, Object &found);

Passa un riferimento alla variabile che riceverà l'oggetto. Il metodo ha generato un'eccezione quando non è possibile trovare un oggetto. Questa convenzione è probabilmente più adatta se non ci si aspetta davvero che un oggetto non venga trovato - quindi si lancia un'eccezione per indicare che si tratta di un caso imprevisto.

Opzione C

bool findObject(Key &key, Object &found);

Il metodo restituisce false quando non è possibile trovare un oggetto. Il vantaggio di questa opzione over A è che puoi verificare il caso di errore in una fase chiara:

if (!findObject(myKey, myObj)) { ...

riferendosi solo al caso in cui null non sia considerato un comportamento eccezionale, sono sicuramente per il metodo try, è chiaro, non è necessario "leggere il libro" o " guarda prima di saltare " come è stato detto qui

quindi sostanzialmente:

bool TryFindObject(RequestParam request, out ResponseParam response)

e questo significa che anche il codice dell'utente sarà chiaro

...
if(TryFindObject(request, out response)
{
  handleSuccess(response)
}
else
{
  handleFailure()
}
...

Se è importante che il codice client conosca la differenza tra found e not found e questo dovrebbe essere un comportamento di routine, è meglio restituire null. Il codice client può quindi decidere cosa fare.

Generalmente dovrebbe restituire null. Il codice che chiama il metodo dovrebbe decidere se generare un'eccezione o tentare qualcos'altro.

O restituisci un'opzione

Un'opzione è fondamentalmente una classe contenitore che forza il client a gestire i casi di stand. Scala ha questo concetto, cerca l'API.

Quindi hai metodi come T getOrElse (T valueIfNull) su questo oggetto per restituire l'oggetto trovato o un oggetto alternativo che specifica il client.

Sfortunatamente JDK è incoerente, se si tenta di accedere alla chiave non esistente nel pacchetto di risorse, non si trova un'eccezione e quando si richiede il valore dalla mappa si ottiene null se non esiste. Quindi cambierei la risposta del vincitore a quanto segue, se il valore trovato può essere null, quindi sollevare un'eccezione quando non viene trovato, altrimenti restituire null. Quindi segui la regola con un'eccezione, se devi sapere perché il valore non viene trovato, solleva sempre l'eccezione, oppure ...

Finché si suppone che restituisca un riferimento all'oggetto, la restituzione di un NULL dovrebbe essere buona.

Tuttavia, se restituisce l'intera cosa insanguinata (come in C ++ se lo fai: 'return blah;' piuttosto che 'return & amp; blah;' (o 'blah' è un puntatore), allora non puoi tornare un NULL, perché non è di tipo "oggetto". In tal caso, lanciare un'eccezione o restituire un oggetto vuoto che non ha un flag di successo impostato è come approccerei il problema.

Non pensare che qualcuno abbia menzionato l'overhead nella gestione delle eccezioni: richiede risorse aggiuntive per caricare ed elaborare l'eccezione, quindi a meno che non si tratti di un vero evento di uccisione di app o di arresto del processo (andare avanti causerebbe più danni che benefici) Opterei per restituendo un valore che l'ambiente chiamante potrebbe interpretare come ritiene opportuno.

Sono d'accordo con quello che sembra essere il consenso qui (restituire null se "non trovato" è un normale risultato possibile, o gettare un'eccezione se la semantica della situazione richiede che l'oggetto sia sempre trovato).

Vi è, tuttavia, una terza possibilità che potrebbe avere senso a seconda della situazione particolare. Il tuo metodo potrebbe restituire un oggetto predefinito di qualche tipo in " non trovato " condizione, consentendo al codice chiamante di essere sicuro che riceverà sempre un oggetto valido senza la necessità di controllo null o rilevazione di eccezioni.

Restituisce un valore nullo, le eccezioni sono esattamente queste: qualcosa che il tuo codice fa non è previsto.

Le eccezioni dovrebbero essere eccezionali . Restituisce null se è valido per restituire un null .

Preferisce restituire null -

Se il chiamante lo utilizza senza controllare, l'eccezione si verifica comunque lì.

Se il chiamante non lo usa davvero, non tassargli un blocco prova / catch

Se il metodo restituisce una raccolta, restituisce una raccolta vuota (come detto sopra). Ma per favore non Collezioni.EMPTY_LIST o simili! (nel caso di Java)

Se il metodo recupera un singolo oggetto, allora hai alcune opzioni.

  1. Se il metodo deve sempre trovare il risultato ed è un vero caso di eccezione per non trovare l'oggetto, allora dovresti generare un'eccezione (in Java: per favore un'eccezione non selezionata)
  2. (solo Java) Se riesci a tollerare che il metodo generi un'eccezione controllata, lancia un ObjectNotFoundException specifico del progetto o simile. In questo caso il compilatore ti dice se ti dimentichi di gestire l'eccezione. (Questa è la mia gestione preferita di cose non trovate in Java.)
  3. Se dici che è davvero ok, se l'oggetto non viene trovato e il nome del tuo metodo è come findBookForAuthorOrReturnNull (..), puoi restituire null. In questo caso si consiglia di fortemente utilizzare una sorta di controllo statico o controllo del compilatore, che impedisce il dereferenziamento del risultato senza un controllo nullo. Nel caso di Java può essere ad es. Trova bug (vedi DefaultAnnotation a http://findbugs.sourceforge.net/manual/annotations.html) o IntelliJ-Checking.

Fai attenzione, se decidi di restituire un valore nullo. Se non sei l'unico programmatore del progetto, otterrai NullPointerExceptions (in Java o in altre lingue) in fase di esecuzione! Quindi non restituire null che non vengono controllati in fase di compilazione.

Se stai utilizzando una libreria o un'altra classe che genera un'eccezione, dovresti riprovare . Ecco un esempio Esempio2.java è come libreria ed Esempio.java usa il suo oggetto. Main.java è un esempio per gestire questa eccezione. Dovresti mostrare un messaggio significativo e (se necessario) impilare la traccia per l'utente nel lato chiamante.

Main.java

public class Main {
public static void main(String[] args) {
    Example example = new Example();

    try {
        Example2 obj = example.doExample();

        if(obj == null){
            System.out.println("Hey object is null!");
        }
    } catch (Exception e) {
        System.out.println("Congratulations, you caught the exception!");
        System.out.println("Here is stack trace:");
        e.printStackTrace();
    }
}
}

Example.java

/**
 * Example.java
 * @author Seval
 * @date 10/22/2014
 */
public class Example {
    /**
     * Returns Example2 object
     * If there is no Example2 object, throws exception
     * 
     * @return obj Example2
     * @throws Exception
     */
    public Example2 doExample() throws Exception {
        try {
            // Get the object
            Example2 obj = new Example2();

            return obj;

        } catch (Exception e) {
            // Log the exception and rethrow
            // Log.logException(e);
            throw e;
        }

    }
}

Example2.java

 /**
 * Example2.java
 * @author Seval
 *
 */
public class Example2 {
    /**
     * Constructor of Example2
     * @throws Exception
     */
    public Example2() throws Exception{
        throw new Exception("Please set the \"obj\"");
    }

}

Dipende molto se ti aspetti di trovare l'oggetto o no. Se segui la scuola di pensiero che dovrebbero essere usate eccezioni per indicare qualcosa, beh, err, si è verificato un eccezionale allora:

  • Oggetto trovato; oggetto restituito
  • Oggetto non trovato; genera eccezione

Altrimenti, restituisce null.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top