Restituisce un design niente male?

Ho sentito alcune voci dire che il controllo di un valore nullo restituito dai metodi è una ctriggers progettazione. Mi piacerebbe sentire alcune ragioni per questo.

pseudocodice:

variable x = object.method() if (x is null) do something 

La logica alla base della mancata restituzione di null è che non è necessario verificarlo e quindi il codice non deve seguire un percorso diverso in base al valore restituito. Potresti voler controllare il pattern a oggetti nulli che fornisce maggiori informazioni al riguardo.

Ad esempio, se dovessi definire un metodo in Java che ha restituito una raccolta, in genere preferirei restituire una raccolta vuota (ad es. Collections.emptyList() ) anziché null poiché significa che il mio codice cliente è più pulito; per esempio

 Collection c = getItems(); // Will never return null. for (Item item : c) { // Will not enter the loop if c is empty. // Process item. } 

… che è più pulito di:

 Collection c = getItems(); // Could potentially return null. // Two possible code paths now so harder to test. if (c != null) { for (Item item : c) { // Process item. } } 

Ecco la ragione.

In Clean Code di Robert Martin scrive che restituire null è una ctriggers progettazione quando è invece ansible restituire, ad esempio, un array vuoto. Dal momento che il risultato atteso è un array, perché no? Ti consentirà di iterare sul risultato senza condizioni aggiuntive. Se è un numero intero, forse 0 sarà sufficiente, se è un hash, hash vuoto. eccetera.

La premessa è di non forzare il codice chiamante per gestire immediatamente i problemi. Il codice di chiamata potrebbe non volersi preoccupare di loro. Questo è anche il motivo per cui in molti casi le eccezioni sono meglio di zero.

Buoni usi di restituzione di null:

  • Se null è un risultato funzionale valido, ad esempio: FindFirstObjectThatNeedsProcessing () può restituire null se non trovato e il chiamante deve controllare di conseguenza.

Uso errato: tentativo di sostituire o hide situazioni eccezionali come:

  • prendere (…) e restituire null
  • Inizializzazione delle dipendenze API fallita
  • Spazio su disco insufficiente
  • Parametri di input non validi (errore di programmazione, gli ingressi devono essere disinfettati dal chiamante)
  • eccetera

In quei casi il lancio di un’eccezione è più adeguato poiché:

  • Un valore di ritorno nullo non fornisce informazioni di errore significative
  • Il chiamante immediato molto probabilmente non può gestire la condizione di errore
  • Non vi è alcuna garanzia che il chiamante stia verificando i risultati nulli

Tuttavia, Eccezioni non devono essere utilizzate per gestire normali condizioni operative del programma quali:

  • Nome utente / password non validi (o qualsiasi input fornito dall’utente)
  • Rottura di loop o come goto non locale

Sì, restituire NULL è un design terribile , in un mondo orientato agli oggetti. In poche parole, l’utilizzo di NULL porta a:

  • gestione degli errori ad hoc (anziché eccezioni)
  • semantico ambiguo
  • lento invece che veloce fallito
  • il pensiero al computer invece del pensiero oggettuale
  • oggetti mutevoli e incompleti

Controlla questo post sul blog per una spiegazione dettagliata: http://www.yegor256.com/2014/05/13/why-null-is-bad.html . Altro nel mio libro Oggetti eleganti , Sezione 4.1.

Chi dice che questo è un cattivo design?

Il controllo dei valori nulli è una pratica comune, persino incoraggiata, altrimenti si corre il rischio di NullReferenceException ovunque. È meglio gestire l’errore con garbo che generare eccezioni quando non è necessario.

Sulla base di quanto hai detto finora, penso che non ci siano abbastanza informazioni.

Restituire un valore null da un metodo CreateWidget () sembra brutto.

Restituire null da un metodo FindFooInBar () sembra corretto.

Il suo inventore dice che è un errore di un miliardo di dollari!

Dipende dalla lingua che stai usando. Se sei in una lingua come C # in cui il modo idiomatico di indicare la mancanza di un valore è restituire null, restituire null è un buon progetto se non hai un valore. In alternativa, in linguaggi come Haskell che utilizza in modo idiatico la monade Maybe per questo caso, restituire null sarebbe un cattivo progetto (se fosse addirittura ansible).

Se leggi tutte le risposte diventa chiaro che la risposta a questa domanda dipende dal tipo di metodo.

In primo luogo, quando accade qualcosa di eccezionale (IOproblem ecc.), Vengono generate eccezioni logicamente. Quando esattamente qualcosa è eccezionale è probabilmente qualcosa per un argomento diverso ..

Ogni volta che ci si aspetta che un metodo non abbia risultati, ci sono due categorie:

  • Se è ansible restituire un valore neutro, farlo .
    Enumrables vuoti, stringhe ecc. Sono buoni esempi
  • Se tale valore neutro non esiste, deve essere restituito null .
    Come accennato, si presume che il metodo non abbia alcun risultato, quindi non è eccezionale, quindi non dovrebbe costituire un’eccezione. Un valore neutro non è ansible (ad esempio: 0 non è un risultato particolarmente neutro, a seconda del programma)

Finché non avremo un modo ufficiale per indicare che una funzione può o non può restituire nulla, provo ad avere una convenzione di denominazione per denotare così.
Proprio come se avessi la convenzione Try Something () per i metodi che dovrebbero fallire, spesso denomino i miei metodi Safe Something () quando il metodo restituisce un risultato neutro invece di null.

Non sono ancora completamente d’accordo con il nome, ma non sono riuscito a trovare nulla di meglio. Quindi sto correndo con quello per ora.

Va bene restituire null se così facendo è significativo in qualche modo:

 public String getEmployeeName(int id){ ..} 

In un caso come questo è significativo restituire null se l’id non corrisponde a un’ quadro esistente, in quanto consente di distinguere il caso in cui non è stata trovata alcuna corrispondenza da un errore legittimo.

Le persone potrebbero pensare che questo sia sbagliato perché può essere abusato come un valore di ritorno “speciale” che indica una condizione di errore, che non è così buona, un po ‘come restituire i codici di errore da una funzione ma confondere perché l’utente deve controllare il ritorno per null, invece di prendere le opportune eccezioni, ad es

 public Integer getId(...){ try{ ... ; return id; } catch(Exception e){ return null;} } 

Le eccezioni sono per circostanze eccezionali .

Se la tua funzione ha lo scopo di trovare un attributo associato a un dato object e tale object non ha tale attributo, potrebbe essere opportuno restituire null. Se l’object non esiste, lanciare un’eccezione può essere più appropriato. Se la funzione ha lo scopo di restituire un elenco di attributi, e non ce ne sono altri da restituire, restituire un elenco vuoto ha senso – stai restituendo tutti gli attributi zero.

Non è necessariamente un cattivo progetto – come con così tante decisioni di progettazione, dipende.

Se il risultato del metodo è qualcosa che non avrebbe un buon risultato nell’uso normale, restituire null va bene:

 object x = GetObjectFromCache(); // return null if it's not in the cache 

Se dovrebbe esserci sempre un risultato non nullo, allora potrebbe essere meglio lanciare un’eccezione:

 try { Controller c = GetController(); // the controller object is central to // the application. If we don't get one, // we're fubar // it's likely that it's OK to not have the try/catch since you won't // be able to really handle the problem here } catch /* ... */ { } 

Per determinati scenari, si desidera notare un errore non appena si verifica.

Controllare contro NULL e non asserire (per errori del programmatore) o lanciare (per errori dell’utente o del chiamante) nel caso di errore può significare che i crash successivi sono più difficili da rintracciare, perché il caso originale non è stato trovato.

Inoltre, ignorare gli errori può portare a degli exploit di sicurezza. Forse la nullità deriva dal fatto che un buffer è stato sovrascritto o simili. Ora non stai andando in crash, il che significa che lo sfruttatore ha la possibilità di eseguire il tuo codice.

Quali alternative vedi restituire null?

Vedo due casi:

  • findAnItem (id). Cosa dovrebbe fare se l’object non viene trovato

In questo caso potremmo: Restituire Null o lanciare un’eccezione (controllata) (o magari creare un object e restituirlo)

  • listItemsMatching (criteri) cosa dovrebbe restituire se non viene trovato nulla?

In questo caso potremmo restituire null, restituire una lista vuota o lanciare un’eccezione.

Credo che il reso nullo possa essere meno buono delle alternative perché richiede al cliente di ricordare di verificare il nulla, i programmatori dimenticano e il codice

 x = find(); x.getField(); // bang null pointer exception 

In Java, lanciando un’eccezione controllata, RecordNotFoundException, consente al compilatore di ricordare al client di occuparsi del caso.

Trovo che le ricerche che restituiscono liste vuote possano essere abbastanza comode: basta compilare il display con tutti i contenuti della lista, oh è vuoto, il codice “funziona”.

Ho una convention in questo settore che mi ha servito bene

Per domande su articoli singoli:

  • Create... restituisce una nuova istanza o genera
  • Get... restituisce un’istanza esistente prevista o genera
  • GetOrCreate... restituisce un’istanza esistente o una nuova istanza se non ne esiste alcuna o genera
  • Find... restituisce un’istanza esistente, se esistente, o null

Per domande di raccolta:

  • Get... restituisce sempre una raccolta, che è vuota se non vengono trovati articoli corrispondenti [1]

[1] dati alcuni criteri, espliciti o impliciti, dati nel nome della funzione o come parametri.

L’idea alla base di questa discussione è programmare in modo difensivo. Cioè, codice contro l’inaspettato. C’è una serie di diverse risposte:

Adamski suggerisce di guardare il modello a oggetti nulli, con la risposta che è stata votata per tale suggerimento.

Michael Valenty suggerisce anche una convenzione di denominazione per dire allo sviluppatore cosa ci si potrebbe aspettare. ZeroConcept suggerisce un uso corretto di Exception, se questo è il motivo del NULL. E altri.

Se facciamo la “regola” che vogliamo sempre fare una programmazione difensiva, allora possiamo vedere che questi suggerimenti sono validi.

Ma abbiamo 2 scenari di sviluppo.

Classi “create” da uno sviluppatore: l’autore

Classi “consumate” da un altro (forse) sviluppatore: lo sviluppatore

Indipendentemente dal fatto che una class restituisca NULL per i metodi con un valore restituito o meno, lo sviluppatore dovrà verificare se l’object è valido.

Se lo sviluppatore non è in grado di farlo, quel metodo / class non è deterministico. Cioè, se la “chiamata di metodo” per ottenere l’object non fa ciò che “pubblicizza” (ad esempio, getEmployee) ha infranto il contratto.

Come autore di una class, voglio sempre essere gentile e difensivo (e deterministico) quando creo un metodo.

Pertanto, dato che NULL o NULL OBJECT (ad esempio se (impiegato come NullEmployee.ISVALID)) deve essere controllato e potrebbe essere necessario che si verifichi con una raccolta di Dipendenti, l’approccio ad oggetti nulli rappresenta l’approccio migliore.

Ma mi piace anche il suggerimento di Michael Valenty di nominare il metodo che DEVE restituire null ad esempio getEmployeeOrNull.

Un autore che lancia un’eccezione sta rimuovendo la scelta dello sviluppatore di testare la validità dell’object, che è molto male su una collezione di oggetti, e costringe lo sviluppatore alla gestione delle eccezioni quando diramano il loro codice di consumo.

Come sviluppatore che consuma la class, spero che l’autore mi dia la possibilità di evitare o programmare per la situazione nulla che la loro class / i metodi potrebbero affrontare.

Quindi, come sviluppatore programmerei in modo difensivo contro NULL da un metodo. Se l’autore mi ha dato un contratto che restituisce sempre un object (NULL OBJECT fa sempre) e quell’object ha un metodo / proprietà con cui testare la validità dell’object, allora userei quel metodo / proprietà per continuare a usare l’object , altrimenti l’object non è valido e non posso usarlo.

La linea di fondo è che l’autore della class / metodi deve fornire meccanismi che uno sviluppatore può utilizzare nella sua programmazione difensiva. Cioè, una più chiara intenzione del metodo.

Lo sviluppatore dovrebbe sempre utilizzare la programmazione difensiva per verificare la validità degli oggetti restituiti da un’altra class / metodo.

Saluti

GregJF

A volte, restituire NULL è la cosa giusta da fare, ma in particolare quando si hanno a che fare con sequenze di tipi diversi (array, liste, stringhe, what-have-you) è probabilmente meglio restituire una sequenza di lunghezza zero, poiché porta a un codice più breve e, auspicabilmente, più comprensibile, senza dedicare molto più spazio alla parte dell’installatore dell’API.

Fagli chiamare un altro metodo dopo il fatto per capire se la precedente chiamata era nulla. 😉 Ehi, è stato abbastanza buono per JDBC

Altre opzioni a questo sono: restituire un valore che indica il successo o meno (o il tipo di errore), ma se hai solo bisogno del valore booleano che indicherà successo / fallimento, restituendo null per errore, e un object per il successo non lo farebbe essere meno corretto, quindi restituire true / false e ottenere l’object tramite parametro.
Un altro approccio dovrebbe usare l’eccezione per indicare i guasti, ma qui – ci sono in realtà molte più voci, che dicono che questa è una pratica CATTIVA (poiché l’uso delle eccezioni può essere conveniente ma presenta molti svantaggi).
Quindi personalmente non vedo nulla di male nel restituire null come indicazione che qualcosa è andato storto, e controllarlo in seguito (per sapere se ci sei riuscito o meno). Inoltre, pensando ciecamente che il tuo metodo non restituirà NULL, e quindi baserai il tuo codice su di esso, potrebbe portare ad altri errori, a volte difficili da trovare (anche se nella maggior parte dei casi arresteranno semplicemente il tuo sistema :), come farai riferimento a 0x00000000 prima o poi).

Durante lo sviluppo di programmi complessi possono insorgere funzioni null non intenzionali e, come il codice morto, tali eventi indicano gravi difetti nelle strutture del programma.

Una funzione o un metodo nullo viene spesso utilizzato come comportamento predefinito di una funzione revocabile o di un metodo sovrascrivibile in un framework di oggetti.

Null_function @wikipedia

Beh, sicuramente dipende dallo scopo del metodo … A volte, una scelta migliore sarebbe quella di lanciare un’eccezione. Tutto dipende da caso a caso.

Se il codice è qualcosa di simile:

 command = get_something_to_do() if command: # if not Null command.execute() 

Se si dispone di un object fittizio il cui metodo execute () non fa nulla e si restituisce tale valore invece di Null nei casi appropriati, non è necessario verificare il caso Null e invece è sufficiente eseguire:

 get_something_to_do().execute() 

Quindi, qui il problema non è tra il controllo di NULL rispetto a un’eccezione, ma è tra il chiamante che deve gestire i non-casi speciali in modo diverso (in qualsiasi modo) o meno.

Per il mio caso d’uso dovevo restituire una mappa dal metodo e poi cercare una chiave specifica. Ma se restituisco una mappa vuota, allora porterà a NullPointerException e quindi non sarà molto diverso restituendo null invece di una mappa vuota. Ma da Java8 in poi potremmo usare Opzionale . Quanto sopra è la ragione per cui è stato introdotto il concetto opzionale.

Buongiorno,

Restituire NULL quando non è ansible creare un nuovo object è una pratica standard per molte API.

Perché diavolo è un cattivo design, non ne ho idea.

Modifica: Questo è vero per le lingue in cui non si hanno eccezioni come C dove è stata la convenzione per molti anni.

HTH

‘Avahappy,