2015-06-08 15 views
14

Sembra esserci una crescente comunità di persone che dice che non si dovrebbe mai restituire null e si dovrebbe sempre utilizzare il modello di oggetto nullo. Riesco a vedere l'utilità del NOP quando si utilizza una raccolta/mappa/array o la chiamata di funzioni booleane come isAuthenticated(), which is shown here.Modello oggetto nullo

Non ho trovato nulla su questo che sia pienamente convincente. Resta con me qui mentre cerco di organizzare i miei pensieri.

La mia comprensione è che invece di restituire un oggetto nullo, si restituisce un oggetto valido che è stato "azzerato".

Così, per esempio, il client sarebbe effettuare una chiamata per ottenere un oggetto:

Car car = getCar(); 

Se non si utilizza il NOP si avrebbe bisogno di controllare se l'oggetto restituito dal getCar() è nullo prima di chiamare qualsiasi metodo su di esso:

if (car != null){ 
    color = car.getColor(); 
    doScreenStuff(color); 
    } 

Utilizzando il NOP, invece di getCar() restituire nulla, ora restituisce un oggetto che è stato effettivamente "azzerato". Così ora non abbiamo più bisogno di fare if (car != null) e possiamo solo richiedere il colore. Quindi, suppongo che il nostro oggetto "azzerato" restituisca "nessuno" quando chiamiamo il colore.

Come può essere d'aiuto? Sembra che andare avanti e chiamare metodi su un oggetto vuoto causi altrettanta sofferenza quanto semplicemente il controllo di null. Ora, quando arriva il momento di visualizzare le informazioni, dobbiamo controllare che il colore non sia "none", che l'altezza non sia 0, o qualunque altro valore tu abbia. Quindi, in sostanza, invece di controllare l'inizio dell'elaborazione se l'auto è nullo, si verifica in seguito se l'oggetto della macchina che abbiamo è un'automobile reale o un sostituto. OSSIA non vogliamo mostrare un mucchio di oggetti vuoti, quindi abbiamo bisogno di un modo per filtrare tutti i nostri oggetti vuoti.

Questo filtro è un passaggio aggiunto, proprio come chiamare se (auto! = Null). L'unica differenza è che con il controllo di null, possiamo arrestare l'elaborazione non appena scopriamo che l'oggetto car è nullo generando un'eccezione, mentre con NOP chiamiamo metodi sull'oggetto vuoto e continuiamo a fare chug finché non arriva il momento di mostra l'oggetto e a questo punto filtriamo i vuoti. Inoltre, è necessario conoscere i valori restituiti dall'oggetto vuoto. OSSIA getColor() restituisce "nessuno" o "vuoto".

Ovviamente deve esserci qualcosa che sto trascurando. Grazie in anticipo.

risposta

6

Il modello di oggetto nullo ha senso solo quando esiste un valore funzionale ragionevole per l'oggetto nullo. Lo scopo non è di rinviare null, come hai descritto, ma di eliminare completamente l'idea di null rappresentando il nulla o il vuoto con un vero pezzo di dati che è ancora funzionale. Ad esempio, il caso naturale di fori in una struttura ad albero, as described in the Wikipedia article.

Un'auto nulla non ha senso. In questo caso, sembra che la cosa più appropriata sia per getCar() restituire Optional<Car>.

0

L'oggetto puntatore nullo, almeno nella mia esperienza, è quindi limitare i controlli nulli a una posizione centrale per evitare eccezioni del puntatore nullo.

Se molti servizi utilizzano CarFactory, è molto più semplice gestire Carfactory con il risultato nullo, quindi gestirlo singolarmente. Inoltre assicura che ogni risultato nullo sia gestito allo stesso modo, sia che non faccia nulla o che abbia una logica specifica.Il rovescio della medaglia è che se non viene gestito correttamente, potrebbe portare ad alcuni bug che creano confusione (soprattutto perché le eccezioni dei puntatori nulli urlano forte e orgoglioso).

Non lo uso più molto. Esistono alternative all'utilizzo di verifiche Null, come l'utilizzo di Java 8 Opzionale. Ci sono persone che sono a favore e contrarie a questo, e questo non è affatto un sostituto per il modello di oggetto nullo.

String result = Optional.ofNullable(someInteger) 
    .map(Integer::valueOf) 
    .map(i -> i + 1) 
    .map(Object::toString) 
    .orElse("number not present"); 

System.out.println(result); 
+0

Il modello di oggetto nullo e facoltativo non hanno lo stesso scopo. Opzionale è usato per indicare che un valore può o non può essere presente. Il modello di oggetto nullo sta semplicemente codificando il fatto che potrebbe esserci un oggetto "niente lì" che ha ancora un comportamento utile. – Cubic

+0

Scusate, forse non mi sono chiarito, stavo dicendo che l'alternativa ai controlli nulli è opzionale, e quindi "questo non è affatto una sostituzione per il modello di oggetto nullo" per il modello di oggetto nullo. Non ho usato il modello di oggetto nullo in un po 'perché non ho avuto un caso di utilizzo recente, ma non intendo assolutamente che gli optionals sostituiscano il modello di oggetto nullo. –

3

Se non vedi il punto, probabilmente non è un buon paradigma da utilizzare per te. L'intera idea della programmazione OO è rendere le cose più semplici per TE. Non rimanere intrappolato nel pensare che è necessario adottare qualcun altro elaborato sistema basato su pattern. Spesso ci vuole una notevole quantità di lavoro per apprendere vari modelli e usarli in modo efficace, quindi è meglio crescere in essi, piuttosto che cercare di costringerti a usarli.

Per quanto riguarda questo particolare modello, si presuppone un certo stile di programmazione che potrebbe essere inappropriato per voi. Non lo userei mai perché restituisco valori null come valori legittimi (dati mancanti) che vengono gestiti in modo diverso in ciascun caso, quindi "gestione centralizzata" non ha senso per me. Quando restituisco i booleani, uso i primitivi.

La linea di fondo qui è che se un modello ti sembra innaturale, non usarlo.

+0

Penso che tu abbia ragione. Sono sempre troppo preoccupato di usare lo schema sbagliato durante la programmazione. In questo caso, sembra che ci siano persone che suggeriscono che stai facendo le cose completamente male se stai restituendo valori null: http://www.yegor256.com/2014/05/13/why-null-is-bad.html – TigerBear

6

La risposta di MattPutnam è giusta e l'ho presa in considerazione. Aggiungo questo: il concetto di "oggetto nullo", quando lo si analizza, sembra ridursi al concetto matematico di uno monoid. Si può pensare in questo modo: un monoide è un tipo che ha entrambe queste cose:

  1. una "aggiungere," "somma" o un'operazione simile, che deve essere associativa: a.op(b).op(c) è lo stesso come a.op(b.op(c)).
  2. Un valore "vuoto", "zero" o "null", che funge da elemento neutro o elemento identità dell'operazione.

L'esempio classico del modello di oggetto nullo consiste nel restituire una lista o matrice vuota anziché null. Bene, le liste sono monoide, con append come operazione e la lista vuota come elemento neutro.

Ora, il problema riscontrato nell'esempio Car è che Car non è realmente un monoide; non vi è alcuna nozione di "macchina vuota" o "auto neutra", e non vi è in realtà un'operazione sensata da utilizzare per combinare due s Car in uno.

Quindi la raccomandazione che stai ricevendo è di utilizzare qualcosa come Java 8 Optional. E il trucco è che non importa quale tipo T è, Optional<T> è un monoide:

  1. del monoide "combinare" operazione è "scegliere il primo valore se non è empty, altrimenti scegliere il secondo valore":
    • x || empty = x
    • empty || x = x
  2. l'elemento neutro è Optional.empty(), perché Optional.empty().orElse(anything) è lo stesso come appena anything.

Quindi, fondamentalmente, Optional<T> è un wrapper che aggiunge un oggetto nullo per tipi come Car che non hanno uno. Il metodo Optional<T>.orElse(T value) che è una versione leggermente refactored del valore "pick first non- empty" monoid.

0

Il modello di progettazione NULL riempie un ASSENZA di un oggetto con un comportamento DEFAULT e dovrebbe essere utilizzato solo quando un oggetto collabora con altro.

Il modello di progettazione NULL non è destinato a sostituire la gestione delle eccezioni NULL. È uno dei vantaggi collaterali del modello di progettazione NULL, ma l'intenzione è fornire un comportamento predefinito.

Ad esempio, considerare il seguente codice di esempio pseduo. È una semplice classe Customer che sta delegando il calcolo dello sconto a un oggetto di sconto.

class Customer { 
    IDiscount dis = new NormalDiscount(); 
    public double CalculateDiscount() { 
     return dis.Calculate(); 
    } 
    // Code removed for simplicity 
} 

Ora diciamo che vogliamo creare clienti che hanno eseguito l'impostazione predefinita per i pagamenti. Quindi ereditiamo dalla classe precedente poiché molte proprietà e comportamenti sono uguali ma non vogliamo il calcolo dello sconto perché un cliente inadempiente non ha diritto agli sconti. Quindi rendiamo nullo l'oggetto sconto, ma questo è un problema perché ora il calcolo dello sconto si bloccherà a causa dell'oggetto sconto NULL.

class DefaultedCustomer : Customer { 
    IDiscount dis = null; 
    public double CalculateDiscount() { 
     return dis.Calculate(); <---- This will crash in parent 
    } 
    // Code removed for simplicity 
} 

Quindi, uno dei modi in cui gli sviluppatori lo correggono è il mio controllo per NULL e restituzione zero. Se chiudi gli occhi e pensi logicamente, questo è in realtà uno scenario aziendale in cui non ci sono sconti per i clienti inadempienti.

Quindi piuttosto che correggere questo tecnicamente controllando NULL possiamo creare una classe di COMPORTAMENTO SCONTO DEFAULT come mostrato di seguito che restituisce lo sconto pari a zero e usa lo stesso per il cliente inadempiente. Questo è più pulito del controllo NULL.

public class DefaultDiscount : IDiscount { 
    public void Calculate() { 
     return 0; 
    } 
} 

NULL sono necessari ma per eccezione non movimentazione per fissare l'assenza di un oggetto in collaborazione come illustrato sopra. Il modello di progettazione NULL non ha senso se non c'è collaborazione.

A causa di NULL progettazione del modello controlli NULL ottenere evitato, ma che è più del dopo effetto e versante delle prestazioni, ma non l'intenzione principale.

Tutti i pensieri di cui sopra ho preso da questo NULL Design pattern in C# che spiega Do e Donazioni di NULL.

-1

La restituzione di Optional on Car non comporta la verifica della presenza o meno di oggetti auto.

Al momento della restituzione dell'auto, verificherebbe se (auto! = Null). Analogamente, quando si accede a Opzionale, DOVREBBE controllare se (car.isPresent()) quindi car.get().

Eseguire un get() senza verificare la presenza non è accettabile e può essere facilmente identificato ed evitato utilizzando la configurazione di tipo check-up per generare errori.

Si potrebbe fare la domanda .. Qual è il vantaggio che stiamo ottenendo a causa di questo se stiamo verificando la sua presenza in entrambi i modelli?

La risposta sta nel sapere che devi fare un controllo, prima di usarlo.Se segui rigorosamente la pratica di restituire Opzionale ovunque possa essere annullabile, allora non è necessario verificare i valori nulli laddove non sia facoltativo.

Agisce come un modo programmatico di documentare metodi che internamente aiuta a far rispettare il controllo tramite checkstyles.

+0

Facoltativo non è lo stesso di Null Object Pattern (https://en.wikipedia.org/wiki/Null_Object_pattern) –

+0

Usando 'Optional' per solo' isPresent() 'e' get() 'è come se avessi solo 20 miglia/ora nel tuo Lamborghini nuovo di zecca. La cosa migliore da fare in questa situazione è 'getCar(). IfPresent (car -> doScreenStuff (car.getColor()))' – Michael

Problemi correlati