2010-10-04 20 views
18

Recentemente ho scoperto un modo interessante per creare una nuova istanza di un oggetto in Google Guava e Project Lombok: Nascondere un costruttore dietro un metodo di creazione statica. Ciò significa che invece di fare new HashBiMap(), devi fare HashBiMap.create().Nascondere un costruttore dietro un metodo di creazione statica?

La mia domanda è perché? Che vantaggio hai di nascondere il costruttore? Per me, non vedo assolutamente alcun vantaggio nel farlo, e sembra violare i principi base della creazione di oggetti. Dall'inizio, crei un oggetto con new Object(), non con il metodo Object.createMe(). Sembra quasi come creare un metodo per creare un metodo.

Cosa guadagni da questo?

+0

Nota che riceverai avvertimenti usando il 'nuovo HashBiMap()', in cui non utilizzerai 'HashBiMap.create()'. Questa è la ragione principale per cui Guava usa questo modello, come ho detto nella mia risposta. – ColinD

risposta

26

Esistono diversi motivi per cui si potrebbe preferire un metodo factory statico anziché un costruttore pubblico. È possibile leggere l'articolo 1 in Effective Java, Second Edition per una discussione più lunga.

  1. Consente al tipo dell'oggetto restituito dal metodo di essere diverso dal tipo della classe che contiene il metodo. Infatti, il tipo restituito può dipendere dai parametri. Ad esempio, EnumSet.of(E) restituirà un tipo diverso se il tipo di emum ha pochissimi elementi vs se il tipo enum ha molti elementi (Modifica: in questo caso particolare, migliorare le prestazioni per il caso comune in cui l'enum non ha molti elementi
  2. Consente il caching. Ad esempio, Integer.valueOf(x) restituirà la stessa istanza dell'oggetto se chiamata più volte con lo stesso valore x, se è compresa tra -128 e 127.
  3. Consente di avere costruttori denominati (che possono essere utili se il tuo la classe ha bisogno di molti costruttori). Vedi, ad esempio, i metodi in java.util.concurrent.Executors.
  4. Consente di creare un'API concettualmente semplice ma in realtà molto potente. Ad esempio, i metodi statici in Collections nascondono molti tipi. Invece di avere una classe Collections con molti metodi statici, avrebbero potuto creare molte classi pubbliche, ma sarebbe stato più difficile per qualcuno di nuovo nella lingua capire o ricordare.
  5. Per i tipi generici, può limitare la quantità di digitazione che è necessario eseguire. Ad esempio, invece di digitare List<String> strings = new ArrayList<String>() in Guava è possibile eseguire List<String> strings = Lists.newArrayList() (il metodo newArrayList è un metodo generico e il tipo del tipo generico viene dedotto).

Per HashBiMap, l'ultima ragione è la più probabile.

+1

Ottima spiegazione! Grazie per l'aiuto – TheLQ

+0

Hmm, sembra che questi siano tutti vantaggi di minore importanza e limite. Poiché i creatori/fabbriche statici volano di fronte a OOP, l'uso sfrenato di questo modello non sembra giustificato. –

+0

@ScottBiggs Non sono d'accordo che i metodi statici di creazione "volano di fronte a OOP."Nota che questi motivi provengono tutti da _Effective Java, Second Edition_ che è la guida definitiva per" le migliori pratiche "per Java, e tutti gli esempi che ho fornito provengono dal JDK – NamshubWriter

3

Questo è chiamato modello metodo di fabbrica. Dove la fabbrica si trova all'interno della classe stessa. Wikipedia describes it pretty well ma qui ci sono alcuni frammenti.

I metodi di produzione sono comuni nei toolkit e nei framework in cui il codice della libreria deve creare oggetti di tipi che possono essere sottoclassati dalle applicazioni che utilizzano il framework.

Le gerarchie di classi parallele spesso richiedono oggetti da una gerarchia per poter creare oggetti appropriati da un altro.

+0

Il pattern factory funziona bene in cose come 'BorderFactory', ma non sto capendo perché si dovrebbe fare il modello factory e la classe in una classe. Cosa stai guadagnando facendo questo? – TheLQ

+0

Vedere: http://code.google.com/p/guava-libraries/source/browse/trunk/src/com/google/common/collect/HashBiMap.java#57 Sono curioso di sapere perché utilizzare questo modello quando realizzi la stessa cosa ma con più codice. – TheLQ

+0

@TheLQ Ho aggiornato la risposta. –

0

Beh, sarebbe possibile per SomeClass.create() estrarre un'istanza da una cache. new SomeClass() non lo farà senza alcuni imbrogli.

Sarebbe inoltre possibile per create() restituire qualsiasi numero di implementazioni di SomeClass. Fondamentalmente, un tipo di fabbrica di dealio.

7

Questo di solito è fatto perché la classe effettivamente istanziata dal metodo create() potrebbe essere diversa dal tipo su cui si sta invocando il metodo. cioè un modello di fabbrica in cui il metodo create() restituisce una sottoclasse specifica che è appropriata dato il contesto corrente. (Ad esempio, restituendo un'istanza quando l'ambiente corrente è Windows e un altro quando è Linux).

+0

Non dovrebbe essere solo per casi speciali? Poiché HashBiMap in Guava non esegue alcun caching speciale, restituisce solo una nuova istanza. E non puoi ignorarlo (o TMK deriderlo) perché è statico. http://code.google.com/p/guava-libraries/source/browse/trunk/src/com/google/common/collect/HashBiMap.java#57 – TheLQ

+1

@TheLQ, non stavo parlando della libreria in particolare . Solo in termini generali su cosa potrebbe essere usato un metodo factory 'create()'. Inoltre, che cosa ha a che fare l'override con qualcosa? Se stai suggerendo che è difficile testare unitamente questo schema, sono completamente d'accordo. –

+0

@Kirk stavo cercando di essere più generale. Dovresti usare questo modello su tutto o solo quando assolutamente necessario per restituire implementazioni specifiche? – TheLQ

0

Sebbene non sia applicabile a questo particolare esempio di codice, la pratica di nascondere il costruttore dietro un metodo statico è Singleton Pattern. Questo è usato quando si vuole assicurare che una singola istanza della classe sia creata e utilizzata in tutto.

+0

Questo è un po 'di quello che stavo ottenendo, ma io Immagina per un singolo metodo di nascondere il costruttore che potresti fare questo. – TheLQ

+0

Anche senza il modello singleton, questo modo di creare oggetti risulta utile se si desidera che l'oggetto sia istanziato in un modo particolare, ad esempio: una mappa hash con una dimensione iniziale ottimizzata invece di affidarsi all'utente API per fornire è per te. –

0

Ci sono molti motivi per utilizzare questo modello di metodo di fabbrica, ma uno dei motivi principali che Guava usa è che consente di evitare di utilizzare due volte i parametri di tipo durante la creazione di una nuova istanza. Confronto:

HashBiMap<Foo, Bar> bimap = new HashBiMap<Foo, Bar>(); 

HashBiMap<Foo, Bar> bimap = HashBiMap.create(); 

Guava fa anche un buon uso del fatto che i metodi di fabbrica possono avere nomi utili, a differenza di costruttori. Considerare ImmutableList.of, ImmutableList.copyOf, Lists.newArrayListWithExpectedSize, ecc.

Si avvale inoltre del fatto che i metodi di fabbrica non devono necessariamente creare un nuovo oggetto. Ad esempio, ImmutableList.copyOf, quando viene fornito un argomento che è esso stesso un ImmutableList, restituirà solo tale argomento anziché effettuare alcuna copia effettiva.

Infine, metodi di fabbrica ImmutableList s' tornano sottoclassi (non pubblici) di ImmutableList quali , SingletonImmutableList e RegularImmutableList a seconda degli argomenti.

Nessuna di queste cose è possibile con i costruttori.

+0

Sono abbastanza sicuro 'HashBiMap bimap = new HashBiMap()' funzionerebbe bene dato che il '' è implicito. – TheLQ

+1

Sarà _work_, sì, ma genererà anche degli avvisi per l'uso del tipo raw invece del tipo parametrizzato ... non dovresti farlo. I metodi statici possono dedurre i parametri del tipo e fare la stessa cosa senza avvisi. – ColinD

+0

Devo anche notare che non è corretto affermare che il '' è implicito. Non è. Stai solo usando il tipo grezzo, lo stile pre-generico. In JDK7, sarai in grado di utilizzare l'operatore diamante, come questo 'Mappa map = new HashMap <>()', che deduce i tipi simili ai metodi statici. – ColinD

6

A differenza dei costruttori, i metodi statici possono avere nomi di metodi. Ecco una recente classe che ho scritto in cui questo è stato utile:

/** 
* A number range that can be min-constrained, max-constrained, 
* both-constrained or unconstrained. 
*/ 

public class Range { 
    private final long min; 
    private final long max; 
    private final boolean hasMin; 
    private final boolean hasMax; 

    private Range(long min, long max, boolean hasMin, boolean hasMax) { 
    // ... (private constructor that just assigns attributes) 
    } 

    // Static factory methods 

    public static Range atLeast (long min) { 
    return new Range(min, 0, true, false); 
    } 

    public static Range atMost (long max) { 
    return new Range(0, max, false, true); 
    } 

    public static Range between (long min, long max) { 
    return new Range(min, max, true, true); 
    } 

    public static Range unconstrained() { 
    return new Range (0, 0, false, false); 
    } 
} 

Non si potrebbe fare questo usando solo i costruttori, come atLeast e atMost avrebbe la firma esattamente lo stesso (entrambi prendono una lunga).

0

ho avuto ragione molto interessante per nascondere costruttore di controllare e per favore fatemi sapere se c'è qualche altra alternativa per raggiungere questo

enter code here 

Class A 
{ 
    String val; 
    protected A() 
    { 
    } 
    protected A(String val) 
    { 
     this.val=val; 
    } 
    protected void setVal(String val) 
    { 
     this.val=val; 
    } 
    public String getVal() 
    { 
     return val; 
    }  
} 
class B extends A 
    { 
    B() 
    { 
     super(); 
    }  
    public val setVal(String val) 
    { 
     super.val=val;  
    } 
} 
class C extends A 
{ 
    C(String val) 
    { 
     super(val); 
    } 
} 
+0

Non hai un metodo di fabbrica in là ... e per essere onesti, non so dove questo codice dovrebbe essere un buon esempio di nascondere il costruttore. Quindi dovresti spiegarlo. – Tom

0

Alcuni motivi principali

  • In primo luogo ti dà il potere per istanziare una diversa (sotto) classe
  • Possibilità di restituire null
  • Consente di restituire un alr eady oggetto esistente
Problemi correlati