Come dovrebbero essere istanziati / strutturati oggetti di valore vuoti / vuoti?

3

Mi stavo chiedendo ... qual è la migliore pratica per creare istanze di valori vuoti? (in Java)

es. Supponiamo di avere qualche valore dell'oggetto classe Foo , che potrebbe essere vuoto.

Creare metodi come questi sarebbe una soluzione pulita? (E forse aggiungendo un'interfaccia "Blankable" o qualcosa del genere).

interface Blankable {
    public Foo blankInstance();
    public boolean isBlank();
}

class Foo implements Blankable {
    public Foo blankInstance();
    public boolean isBlank();
}

Foo foo = something();
if (foo.isBlank()) // Tell the user

O sarebbe meglio qualcosa di più semplice come questo?

class Foo {
    public static Foo BLANK = /* Some constructor */;
}

Foo foo = something();
if (foo == Foo.BLANK) // Tell the user

Mi sto appoggiando al primo esempio, ma non sono ancora sicuro se si tratta di una tecnica ampiamente accettata.

Mi sento un po 'resistente a (ma ancora aperto a) usando l'opzione Guava opzionale < T > classe perché sembra una soluzione alternativa in questa circostanza.

    
posta elimirks 06.01.2015 - 19:04
fonte

2 risposte

2

Una soluzione come Option<T> non è una cattiva idea. Infatti, è stato introdotto con successo in Haskell come Forse ( ma non so se questa è la prima volta che questa soluzione è stata utilizzata). Le forse monadi sono comuni nei programmi Haskell. Dal momento che altre lingue hanno adottato lo stesso sistema. Ad esempio, Scala ha un tipo di opzione .

Se non vuoi diventare dipendente da Guava, puoi usare Java 8, che introduce Optional class.

Tuttavia, i tipi Option / Maybe / ... sono più rilevanti in un ambiente funzionale, perché ciò che in genere si vuole fare è ottenere un comportamento / una proprietà dall'elemento se esiste realmente, e non ottenere nulla o un comportamento predefinito / proprietà altrimenti. Quindi, un uso tipico di Opzioni consiste in

  • applicare una funzione A- > B su Option[A] per ottenere un Option[B] ;
  • cambiare il codice sulla base del contenuto reale dell'opzione (ad esempio, utilizzando la corrispondenza Pattern se il linguaggio lo supporta e se Option ha due sottotipi o utilizzando il metodo overload);
  • o filtro delle Opzioni che rappresentano elementi (non) esistenti.

In alternativa, puoi utilizzare il Pattern di oggetto nullo , in cui hai una classe astratta MyClass e due sottoclassi concrete: MyRealClass e MyNullClass . Si manipolano istanze di MyClass , ma si generano istanze di MyRealClass (se l'elemento è realmente esistente) o MyNullClass (se l'elemento non esiste ). MyNullClass contiene i comportamenti / proprietà predefiniti. Se gli oggetti nulli sono stateless (che di solito è il caso), uno potrebbe memorizzarli nella cache o persino renderli singleton.

Questo modello è descritto in [Fowler].

[Fowler] Martin Fowler, Kent Beck, Refactoring: migliorare la progettazione del codice esistente .

    
risposta data 07.01.2015 - 09:11
fonte
2

L'intero punto di un oggetto valore è che l'uguaglianza non è basata sull'identità; potresti benissimo avere due distinti oggetti vuoti. Infatti, se tutti gli oggetti vuoti sono gli stessi o oggetti diversi dovrebbero essere un dettaglio di implementazione. Per questi motivi, il secondo approccio non è buono. Se si guardano le API Java 8, esiste una nozione di una "classe basata sul valore" con le seguenti proprietà:

  • sono definitivi e immutabili (sebbene possano contenere riferimenti a oggetti mutabili);
  • hanno implementazioni di equals, hashCode e toString che sono calcolate esclusivamente dallo stato dell'istanza e non dalla sua identità o dallo stato di qualsiasi altro oggetto o variabile;
  • non fare uso di operazioni sensibili all'identità come l'uguaglianza di riferimento (==) tra le istanze, il codice hash dell'identità delle istanze o la sincronizzazione sul blocco intrinseco di un'istanza;
  • sono considerati uguali basati esclusivamente su equals (), non basati sull'uguaglianza di riferimento (==);
  • non hanno costruttori accessibili, ma sono invece istanziati attraverso metodi factory che non prevedono alcun impegno sull'identità delle istanze restituite;
  • sono liberamente sostituibili quando sono uguali, il che significa che lo scambio di due istanze xey uguali secondo equals () in qualsiasi computazione o invocazione di metodo non dovrebbe produrre alcun cambiamento visibile nel comportamento.

La documentazione aggiunge inoltre:

A program may produce unpredictable results if it attempts to distinguish two references to equal values of a value-based class, whether directly via reference equality or indirectly via an appeal to synchronization, identity hashing, serialization, or any other identity-sensitive mechanism. Use of such identity-sensitive operations on instances of value-based classes may have unpredictable effects and should be avoided.

Non c'è modo di disattivare l'operatore == in Java in modo che l'avviso sia il meglio che puoi fare.

Quindi, il tuo primo approccio è corretto, con l'avvertenza che foo1.equals(foo2) dovrebbe sempre essere true quando foo1.isBlank() e foo2.isBlank() anche se foo1 != foo2 .

    
risposta data 06.01.2015 - 19:32
fonte

Leggi altre domande sui tag