2010-11-05 6 views
5

Io e un collega stanno avendo un dibattito:Singleton design di classe - oggetto null

abbiamo una classe Singleton che viene utilizzato in più posti nel nostro codice di base.

In origine, la classe è stata progettata in modo tale che si potrebbe ottenere l'oggetto di classe, ma che oggetto non sarebbe completamente inizializzato.

Con questo voglio dire:

mySingleton.getInstance().SomeMethod(); 

SomeMethod causerebbe errori perché la classe non è stata inizializzata. Per la classe per funzionare correttamente, questo dovrebbe accadere:

mySingleton.getInstance().initObject(); 

mySingleton.getInstance().SomeMethod(); 

In ogni modo, il dibattito che ho è che il costruttore (chiamato con la prima istanza ottenere) dovrebbe chiamare initObject in modo che nessun errore possono essere gettati.

Cosa ne pensi?

mio collega piace l'altro modo per cui sa quando la classe è sempre inizializzato. (per esempio chiama initObject a 1 riga di codice specifica e spera che nient'altro abbia bisogno di prima).

+3

In molti casi, la * migliore * soluzione è semplicemente non utilizzare un Singleton in primo luogo - invece, utilizzare un normale oggetto di istanza e semplicemente iniettare l'istanza in classi che ne hanno bisogno (o passarla manualmente nel costruttore o alcuni di questi, o utilizzando un framework IoC). –

risposta

1

A mio parere avere un metodo initObject() è inutile e sembra sconfiggere lo scopo di avere un Singleton. Sento che l'assunzione di chiunque utilizzi il vostro Singleton si assume che quando chiamano getInstance() che otterranno un'istanza completamente funzionale della classe. Perché complicare le cose e aggiungere confusione al codice richiedendo a qualcuno di chiamare initObject() prima che possano persino usarlo. Perché qualcuno deve sapere quando l'oggetto è inizializzato, non si dovrebbe presumere che l'oggetto recuperato sia già inizializzato?

8

sei più vicino al modo usuale che il pattern Singleton è implementato in Java che il vostro collega. Si prega di dare un'occhiata a Wikipedia. Vi si possono trovare i 3 implementazioni Java più comuni:

modo semplice tradizionale

public class Singleton { 
    private static final Singleton INSTANCE = new Singleton(); 

    // Private constructor prevents instantiation from other classes 
    private Singleton() {} 

    public static Singleton getInstance() { 
     return INSTANCE; 
    } 
} 

La "soluzione di Bill Pugh"

public class Singleton { 
    // Private constructor prevents instantiation from other classes 
    private Singleton() {} 

    /** 
    * SingletonHolder is loaded on the first execution of Singleton.getInstance() 
    * or the first access to SingletonHolder.INSTANCE, not before. 
    */ 
    private static class SingletonHolder { 
     public static final Singleton INSTANCE = new Singleton(); 
    } 

    public static Singleton getInstance() { 
     return SingletonHolder.INSTANCE; 
    } 
} 

modo tradizionale semplice utilizzando la sincronizzazione

public class Singleton { 
    // volatile is needed so that multiple threads can reconcile the instance 
    // semantics for volatile changed in Java 5. 
    private volatile static Singleton singleton; 

    private Singleton() {} 

    // synchronized keyword has been removed from here 
    public static Singleton getSingleton() { 
     // needed because once there is singleton available no need to acquire 
     // monitor again & again as it is costly 
     if (singleton == null) { 
      synchronized (Singleton.class) { 
       // this is needed if two threads are waiting at the monitor at the 
       // time when singleton was getting instantiated 
       if (singleton == null) { 
        singleton = new Singleton(); 
       } 
      } 
     } 
     return singleton; 
    } 
} 

Nessuno di loro fanno uso di un metodo initObject() separato (i la nitializzazione dovrebbe essere all'interno del costruttore privato). Si noti inoltre che se si dispone di un metodo pubblico separato, initObject(), si potrebbero avere problemi di multi-threading ...

BTW, personalmente preferisco usare l'alternativa "Bill Pugh", ma i 3 modi sono validi.

Modifica Dopo il genere Esko commento, sto aggiungendo la seguente implementazione, che non è disponibile su Wikipedia. Vorrei solo aggiungere che 1) L'istanza singleton non viene creata pigramente come le 3 opzioni sopra; 2) Poiché si tratta di una enumerazione, non è possibile estendere alcuna classe; e 3) È molto, molto strano.Ma sembra essere abbastanza pubblicizzato sulla comunità Java, quindi è qui:

Enum modo

public enum Singleton { 
    INSTANCE; 
    Singleton() { 
     /* Your init code goes here */ 
    } 
} 
+2

Nessuno di questi, curiosamente, usa enum che è il modo più semplice per implementare un singleton in Java. – Esko

+2

@Esko: in effetti trovo l'implementazione enum un po 'strana semanticamente (fondamentalmente un WTF Java), ma OK, ho intenzione di modificare la mia risposta per aggiungerla ... – rsenna

1

vorrei mettere un controllo di inizializzazione nel metodo getInstance():

public static MySingleton getInstance() { 
    if (! isInitialized) 
    initialize(); 
    return instance; 
} 

... e rendere privato il metodo initialize().

EDIT: A quanto pare ho frainteso la domanda. Il problema principale è se initObject() deve essere all'interno o all'esterno del costruttore. La risposta corretta può dipendere dal problema reale, ma penso che dovrebbe essere generalmente all'interno del costruttore, perché se l'unico scopo di mettere il metodo initObject() all'esterno del costruttore è dire a tutti dove avviene l'inizializzazione, si potrebbe anche scrivere un commento:

// Attention everyone!! Object is initialized here!!1!11! 
MySingleton instance = MySingleton.getInstance(); 

// Object is NOT initialized here!!11!1!! 
MySingleton instance2 = MySingleton.getInstance(); 
+4

Se vuoi seguire quella strada, tu deve piazzare quel controllo di inizializzazione all'interno di un blocco 'sincronizzato' (per ottenere sicurezza sui thread). – rsenna

+0

+1 per "se il solo scopo di mettere il metodo initObject() all'esterno del costruttore è dire a tutti dove avviene l'inizializzazione, si potrebbe anche scrivere un commento". –

0

Il tuo approccio è più vicino all'idea di Singleton, tanto per cominciare.

Le informazioni di inizializzazione devono essere trasparenti per l'utente o gli utenti di Singleton. Un buon modo per farlo non è nemmeno usare un costruttore, ma inserire il codice initObject all'interno di un blocco statico, all'inizio della definizione della classe.

0

Non capisco perché sia ​​necessario un metodo initObject. Se questo è un singleton, quel metodo riceverà la chiamata solo una volta in ogni caso. Vorrei solo inline il metodo all'interno del costruttore stesso.

+0

Grazie Team, sono d'accordo con tutti voi. Penso che initObject sia il risultato di qualche frustrante debugging ... o qualcosa del genere ... Comunque, è bello ricevere feedback. Grazie ancora. –

0

Se è possibile evitarlo, non disporre di un metodo initObject.

Potrebbe risultare inevitabile in due situazioni:

  • L'inizializzazione del singleton ha accadere in un punto particolare durante l'avvio dell'applicazione, e questo è l'unico modo ragionevole per imporre che accada .

  • L'inizializzazione del singleton richiede i parametri che possono essere forniti solo in questo modo; per esempio. parametri costruiti da argomenti della riga di comando.

Se è necessario disporre di un metodo di inizializzazione, deve generare un'eccezione se viene chiamato due volte. Potrebbe anche essere una buona idea per il metodo getInstance() lanciare un'eccezione se viene chiamata troppo presto.

Questi devono essere applicati all'esempio "tradizionale" nella risposta di @ rsenna.

+0

Grazie Team, sono d'accordo con tutti voi. Penso che initObject sia il risultato di qualche frustrante debugging ... o qualcosa del genere ... Comunque, è bello ricevere feedback. –