it-swarm.it

Qual è un modo efficace per implementare un modello singleton in Java?

Qual è un modo efficace per implementare un modello singleton in Java?

752

Usa un enum:

public enum Foo {
    INSTANCE;
}

Joshua Bloch ha spiegato questo approccio nel suo Effective Java Reloaded talk a Google I/O 2008: link a video . Vedi anche le diapositive 30-32 della sua presentazione ( effective_Java_reloaded.pdf ):

Il modo giusto per implementare un Singleton serializzabile

public enum Elvis {
    INSTANCE;
    private final String[] favoriteSongs =
        { "Hound Dog", "Heartbreak Hotel" };
    public void printFavorites() {
        System.out.println(Arrays.toString(favoriteSongs));
    }
}

Modifica: Una porzione online di "Effective Java" dice: 

"Questo approccio è funzionalmente equivalente all'approccio del campo pubblico, tranne che è più conciso, fornisce il meccanismo di serializzazione gratuito e fornisce una garanzia ferrea contro l'istanziazione multipla, anche di fronte a sofisticati attacchi di serializzazione o riflessione. ancora per essere ampiamente adottato, un tipo di enum a elemento singolo è il modo migliore per implementare un singleton. "

743
Stephen Denne

A seconda dell'utilizzo, ci sono diverse risposte "corrette".

Dal momento che Java5 il modo migliore per farlo è usare un enum:

public enum Foo {
   INSTANCE;
}

Pre Java5, il caso più semplice è:

public final class Foo {

    private static final Foo INSTANCE = new Foo();

    private Foo() {
        if (INSTANCE != null) {
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        return INSTANCE;
    }

    public Object clone() throws CloneNotSupportedException{
        throw new CloneNotSupportedException("Cannot clone instance of this class");
    }
}

Andiamo oltre il codice. Primo, vuoi che la lezione sia definitiva. In questo caso, ho usato la parola chiave final per far sapere agli utenti che è definitiva. Quindi è necessario rendere privato il costruttore per impedire agli utenti di creare il proprio Foo. Lanciare un'eccezione dal costruttore impedisce agli utenti di usare il riflesso per creare un secondo Foo. Quindi crei un campo private static final Foo per contenere l'unica istanza e un metodo public static Foo getInstance() per restituirlo. Le specifiche Java assicurano che il costruttore venga chiamato solo quando la classe viene utilizzata per la prima volta.

Quando si dispone di un oggetto molto grande o di un codice di costruzione pesante, nonché di altri metodi o campi statici accessibili che potrebbero essere utilizzati prima che sia necessaria un'istanza, solo allora è necessario utilizzare l'inizializzazione pigra.

Puoi usare un private static class per caricare l'istanza. Il codice sarebbe quindi simile a:

public final class Foo {

    private static class FooLoader {
        private static final Foo INSTANCE = new Foo();
    }

    private Foo() {
        if (FooLoader.INSTANCE != null) {
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        return FooLoader.INSTANCE;
    }
}

Poiché la riga private static final Foo INSTANCE = new Foo(); viene eseguita solo quando viene effettivamente utilizzata la classe FooLoader, questo si occupa dell'istanza lazy e garantisce che sia thread-safe.

Quando vuoi anche essere in grado di serializzare il tuo oggetto devi assicurarti che la deserializzazione non crei una copia.

public final class Foo implements Serializable {

    private static final long serialVersionUID = 1L;

    private static class FooLoader {
        private static final Foo INSTANCE = new Foo();
    }

    private Foo() {
        if (FooLoader.INSTANCE != null) {
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        return FooLoader.INSTANCE;
    }

    @SuppressWarnings("unused")
    private Foo readResolve() {
        return FooLoader.INSTANCE;
    }
}

Il metodo readResolve() si accerterà che venga restituita l'unica istanza, anche quando l'oggetto è stato serializzato in una precedente esecuzione del programma.

224
Roel Spilker

Disclaimer: Ho appena riassunto tutte le risposte fantastiche e l'ho scritto nelle mie parole.


Durante l'implementazione di Singleton abbiamo 2 opzioni
1. Caricamento lento
2. Caricamento iniziale

Il caricamento lento aggiunge bit overhead (molto onesto) quindi usalo solo quando hai un oggetto molto grande o un codice di costruzione pesante e hai anche altri metodi statici accessibili o campi che potrebbero essere utilizzati prima che sia necessaria un'istanza, quindi e solo allora è necessario utilizzare l'inizializzazione pigra. Altrimenti, scegliere il caricamento anticipato è una buona scelta.

Il modo più semplice di implementare Singleton è 

public class Foo {

    // It will be our sole hero
    private static final Foo INSTANCE = new Foo();

    private Foo() {
        if (INSTANCE != null) {
            // SHOUT
            throw new IllegalStateException("Already instantiated");
        }
    }

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

Tutto è buono tranne il suo singleton caricato in anticipo. Proviamo pigro caricato singleton

class Foo {

    // Our now_null_but_going_to_be sole hero 
    private static Foo INSTANCE = null;

    private Foo() {
        if (INSTANCE != null) {
            // SHOUT  
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        // Creating only  when required.
        if (INSTANCE == null) {
            INSTANCE = new Foo();
        }
        return INSTANCE;
    }
}

Fin qui tutto bene, ma il nostro eroe non sopravviverà combattendo da solo con molteplici thread malvagi che vogliono molti esempi del nostro eroe .... Quindi, proteggiamolo dal malvagio multi-threading

class Foo {

    private static Foo INSTANCE = null;

    // TODO Add private shouting constructor

    public static Foo getInstance() {
        // No more tension of threads
        synchronized (Foo.class) {
            if (INSTANCE == null) {
                INSTANCE = new Foo();
            }
        }
        return INSTANCE;
    }
}

ma non è abbastanza per proteggere l'eroe, Davvero !!! Questo è il meglio che possiamo/dovremmo fare per aiutare il nostro eroe 

class Foo {

    // Pay attention to volatile
    private static volatile Foo INSTANCE = null;

    // TODO Add private shouting constructor

    public static Foo getInstance() {
        if (INSTANCE == null) { // Check 1
            synchronized (Foo.class) {
                if (INSTANCE == null) { // Check 2
                    INSTANCE = new Foo();
                }
            }
        }
        return INSTANCE;
    }
}

Questo è chiamato "idioma di blocco a doppio controllo". È facile dimenticare la dichiarazione volatile e difficile capire perché sia ​​necessario.
Per dettagli: http://www.cs.umd.edu/~pugh/Java/memoryModel/DoubleCheckedLocking.html

Ora siamo sicuri del filo del male, ma per quanto riguarda la serializzazione crudele? Dobbiamo assicurarci che anche durante la de-serializzazione non venga creato alcun nuovo oggetto

class Foo implements Serializable {

    private static final long serialVersionUID = 1L;

    private static volatile Foo INSTANCE = null;

    // Rest of the things are same as above

    // No more fear of serialization
    @SuppressWarnings("unused")
    private Object readResolve() {
        return INSTANCE;
    }
}

Il metodo readResolve() si accerterà che venga restituita l'unica istanza, anche quando l'oggetto è stato serializzato in una precedente esecuzione del nostro programma.

Infine abbiamo aggiunto una protezione sufficiente contro i thread e la serializzazione, ma il nostro codice sembra essere ingombrante e brutto. Lasciamo che il nostro eroe faccia una fine

public final class Foo implements Serializable {

    private static final long serialVersionUID = 1L;

    // Wrapped in a inner static class so that loaded only when required
    private static class FooLoader {

        // And no more fear of threads
        private static final Foo INSTANCE = new Foo();
    }

    // TODO add private shouting construcor

    public static Foo getInstance() {
        return FooLoader.INSTANCE;
    }

    // Damn you serialization
    @SuppressWarnings("unused")
    private Foo readResolve() {
        return FooLoader.INSTANCE;
    }
}

Sì, questo è il nostro stesso eroe :)
Poiché la riga private static final Foo INSTANCE = new Foo(); viene eseguita solo quando viene effettivamente utilizzata la classe FooLoader, questo si occupa dell'istanza lazy, 

ed è garantito per essere thread-safe.

E siamo arrivati ​​così lontano, ecco il modo migliore per ottenere tutto ciò che abbiamo fatto è il modo migliore possibile 

 public enum Foo {
       INSTANCE;
   }

Che internamente sarà trattato come 

public class Foo {

    // It will be our sole hero
    private static final Foo INSTANCE = new Foo();
}

Non c'è più paura della serializzazione, fili e codice brutto. Anche ENUMS singleton sono pigramente inizializzati

Questo approccio è funzionalmente equivalente all'approccio del campo pubblico, tranne che è più conciso, fornisce il meccanismo di serializzazione gratuitamente e fornisce una garanzia ferrea contro più istanziazione, anche di fronte a sofisticate serializzazioni o attacchi di riflessione. Mentre questo approccio deve ancora essere ampiamente adottato, un tipo di enum a elemento singolo è il modo migliore per implementare un singleton.

-Joshua Bloch in "Effective Java" 

Ora potresti aver capito perché ENUMS è considerato il modo migliore per implementare Singleton e grazie per la tua pazienza :)
Aggiornato sul mio blog

127
xyz

La soluzione pubblicata da Stu Thompson è valida in Java5.0 e versioni successive. Ma preferirei non usarlo perché penso che sia soggetto a errori.

È facile dimenticare la dichiarazione volatile e difficile capire perché sia ​​necessario. Senza la volatilità, questo codice non sarebbe più sicuro per il thread grazie all'antenna antifurto a doppio controllo. Per ulteriori informazioni, consultare il paragrafo 16.2.4 di Concurrency in Practice in Java . In breve: questo pattern (precedente a Java5.0 o senza l'istruzione volatile) potrebbe restituire un riferimento all'oggetto Bar che è (ancora) in uno stato errato.

Questo modello è stato inventato per l'ottimizzazione delle prestazioni. Ma questa non è più una vera preoccupazione. Il seguente codice di inizializzazione pigro è veloce e, soprattutto, più facile da leggere.

class Bar {
    private static class BarHolder {
        public static Bar bar = new Bar();
    }

    public static Bar getBar() {
        return BarHolder.bar;
    }
}
121
Benno Richters

Thread sicuro in Java 5+:

class Foo {
    private static volatile Bar bar = null;
    public static Bar getBar() {
        if (bar == null) {
            synchronized(Foo.class) {
                if (bar == null)
                    bar = new Bar(); 
            }
        }
        return bar;
    }
}

EDIT: fai attenzione al modificatore volatile qui. :) È importante perché senza di esso, altri thread non sono garantiti dal JMM (Java Memory Model) per vedere le modifiche al suo valore. La sincronizzazione non si occupa di questo: serializza solo l'accesso a quel blocco di codice.

EDIT 2 : @ La risposta di Bno descrive l'approccio raccomandato da Bill Pugh (FindBugs) ed è discutibile meglio. Leggi e vota anche la sua risposta.

94
Stu Thompson

Dimentica inizializzazione pigra , è troppo problematico. Questa è la soluzione più semplice:

public class A {    

    private static final A INSTANCE = new A();

    private A() {}

    public static A getInstance() {
        return INSTANCE;
    }
}
90
Jonathan

Assicurati di averne davvero bisogno. Fai un google per "singleton anti-pattern" per vedere alcuni argomenti contro di esso. Non c'è niente di intrinsecamente sbagliato in esso, suppongo, ma è solo un meccanismo per esporre alcuni dati/risorse globali, quindi assicurati che questo sia il modo migliore. In particolare, ho trovato che l'iniezione di dipendenza è più utile in particolare se si utilizzano anche i test unitari perché DI consente di utilizzare risorse fittizie a scopo di test.

47
Neil Burroughs

Non dimenticare che Singleton è solo un Singleton per il Classloader che lo ha caricato. Se si utilizzano più caricatori (contenitori), ogni COULD dispone della propria versione di Singleton.

26
Javamann

Sono confuso da alcune delle risposte che suggeriscono DI come alternativa all'uso dei singleton; questi sono concetti non correlati. È possibile utilizzare DI per iniettare istanze singleton o non-singleton (ad esempio per-thread). Almeno questo è vero se usi Spring 2.x, non posso parlare per altri framework DI.

Quindi la mia risposta all'OP sarebbe (in tutto tranne il codice di esempio più banale) per:

  1. Utilizzare un framework DI come Spring, quindi
  2. Rendilo parte della tua configurazione DI se le tue dipendenze sono singleton, scope scope, scope della sessione o altro.

Questo approccio offre un'architettura Nice disaccoppiata (e quindi flessibile e testabile) in cui usare un singleton è un dettaglio di implementazione facilmente reversibile (a condizione che i singoli che usi siano sicuri, ovviamente).

21
Andrew Swan

Considerate davvero perché avete bisogno di un singleton prima di scriverlo. C'è un dibattito quasi religioso sul loro utilizzo che puoi facilmente inciampare se tu vai a singletons su Java.

Personalmente cerco di evitare i singleton il più spesso possibile per molte ragioni, di nuovo la maggior parte delle quali può essere trovata da singoloni googling. Sento che spesso i singleton sono abusati perché sono facili da capire da tutti, vengono usati come meccanismo per ottenere dati "globali" in un design OO e vengono usati perché è facile aggirare l'oggetto gestione del ciclo di vita (o veramente pensando a come si può fare A dall'interno di B). Guarda cose come Inversion of Control (IoC) o Dependency Injection (DI) per un middleground Nice.

Se hai davvero bisogno di uno, allora wikipedia ha un buon esempio di una corretta implementazione di un singleton.

20
Aidos

Di seguito sono 3 diversi approcci

1) Enum

/**
* Singleton pattern example using Java Enumj
*/
public enum EasySingleton{
    INSTANCE;
}

2) Doppio controllo Bloccaggio/caricamento lento

/**
* Singleton pattern example with Double checked Locking
*/
public class DoubleCheckedLockingSingleton{
     private static volatile DoubleCheckedLockingSingleton INSTANCE;

     private DoubleCheckedLockingSingleton(){}

     public static DoubleCheckedLockingSingleton getInstance(){
         if(INSTANCE == null){
            synchronized(DoubleCheckedLockingSingleton.class){
                //double checking Singleton instance
                if(INSTANCE == null){
                    INSTANCE = new DoubleCheckedLockingSingleton();
                }
            }
         }
         return INSTANCE;
     }
}

3) Metodo statico di fabbrica

/**
* Singleton pattern example with static factory method
*/

public class Singleton{
    //initailzed during class loading
    private static final Singleton INSTANCE = new Singleton();

    //to prevent creating another instance of Singleton
    private Singleton(){}

    public static Singleton getSingleton(){
        return INSTANCE;
    }
}
16
Abhijit Gaikwad

Uso il Framework di primavera per gestire i miei singleton. Non impone il "singleton-ness" della classe (che non si può fare comunque se ci sono più caricatori di classe coinvolti) ma fornisce un modo davvero semplice per costruire e configurare diverse fabbriche per creare diversi tipi di oggetti.

13
Matt

Versione 1:

public class MySingleton {
    private static MySingleton instance = null;
    private MySingleton() {}
    public static synchronized MySingleton getInstance() {
        if(instance == null) {
            instance = new MySingleton();
        }
        return instance;
    }
}

Caricamento lento, thread safe con blocco, prestazioni ridotte a causa di synchronized.

Versione 2:

public class MySingleton {
    private MySingleton() {}
    private static class MySingletonHolder {
        public final static MySingleton instance = new MySingleton();
    }
    public static MySingleton getInstance() {
        return MySingletonHolder.instance;
    }
}

Caricamento lento, thread safe con non bloccante, alte prestazioni.

11
coderz

Wikipedia ha alcuni esempi di singleton, anche in Java. L'implementazione di Java 5 sembra abbastanza completa ed è thread-safe (applicato il blocco a doppio controllo).

10
macbirdie

Se non hai bisogno di caricare pigro, prova semplicemente

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

    private Singleton() {}

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

    protected Object clone() {
        throw new CloneNotSupportedException();
    }
}

Se si desidera un caricamento lento e si desidera che Singleton sia sicuro per i thread, provare il modello di verifica doppia 

public class Singleton {
        private static Singleton instance = null;

        private Singleton() {}

        public static Singleton getInstance() { 
              if(null == instance) {
                  synchronized(Singleton.class) {
                      if(null == instance) {
                          instance = new Singleton();
                      }
                  }
               }
               return instance;
        }

        protected Object clone() {
            throw new CloneNotSupportedException();
        }
}

Dato che non è garantito il funzionamento del pattern a doppio controllo (a causa di qualche problema con i compilatori, non so nient'altro a riguardo), potresti anche provare a sincronizzare l'intero metodo getInstance o creare un registro per tutti i tuoi Singletons. 

10
Aleksi Yrttiaho

Direi enum singleton 

Singleton che usa enum in Java è generalmente un modo per dichiarare enum singleton. Enum singleton può contenere variabili di istanza e metodo di istanza. Per semplicità, si noti inoltre che se si utilizza un metodo di istanza di quello necessario per garantire la sicurezza del thread di quel metodo, se non influisce sullo stato dell'oggetto.

L'uso di un enum è molto facile da implementare e non ha inconvenienti riguardo agli oggetti serializzabili, che devono essere aggirati negli altri modi.

/**
* Singleton pattern example using Java Enum
*/
public enum Singleton {
        INSTANCE;
        public void execute (String arg) {
                //perform operation here
        }
}

Puoi accedervi con Singleton.INSTANCE, molto più facile che chiamare il metodo getInstance() su Singleton.

1.12 Serializzazione di costanti Enum

Le costanti Enum sono serializzate in modo diverso rispetto agli ordinari oggetti serializzabili o esternalizzabili. La forma serializzata di una costante enum consiste unicamente del suo nome; i valori di campo della costante non sono presenti nel modulo. Per serializzare una costante enum, ObjectOutputStream scrive il valore restituito dal metodo del nome della costante enum. Per deserializzare una costante enum, ObjectInputStream legge il nome costante dallo stream; la costante deserializzata viene quindi ottenuta chiamando il metodo Java.lang.Enum.valueOf, passando il tipo di enum della costante insieme al nome della costante ricevuta come argomenti. Come altri oggetti serializzabili o esternalizzabili, le costanti enum possono funzionare come target di riferimenti posteriori che appaiono successivamente nel flusso di serializzazione.

Il processo di serializzazione delle costanti di enumerazione non può essere personalizzato: tutti i metodi writeObject, readObject, readObjectNoData, writeReplace e readResolve specifici della classe definiti dai tipi di enumerazione vengono ignorati durante la serializzazione e la deserializzazione. Allo stesso modo, anche le dichiarazioni di campo serialPersistentFields o serialVersionUID vengono ignorate - tutti i tipi di enum hanno un serialVersionUID fisso di 0L. Non è necessario documentare campi serializzabili e dati per tipi di enumerazione, poiché non vi è alcuna variazione nel tipo di dati inviati.

Citato dai documenti Oracle

Un altro problema con i singleton convenzionali è che una volta implementata l'interfaccia Serializable, non rimangono più Singleton perché il metodo readObject() restituisce sempre una nuova istanza come il costruttore in Java. Questo può essere evitato usando readResolve() e scartando l'istanza appena creata sostituendo con singleton come sotto 

 // readResolve to prevent another instance of Singleton
 private Object readResolve(){
     return INSTANCE;
 }

Questo può diventare ancora più complesso se la classe Singleton mantiene lo stato, in quanto è necessario renderli transitori, ma con Enum Singleton, la serializzazione è garantita da JVM.


Buona lettura

  1. Singleton Pattern
  2. Enums, Singletons and Deserialization
  3. Bloccaggio a doppia verifica e modello Singleton
8
NullPoiиteя
There are 4 ways to create a singleton in Java.

1- eager initialization singleton

    public class Test{
        private static final Test test = new Test();
        private Test(){}
        public static Test getTest(){
            return test;
        }
    }

2- lazy initialization singleton (thread safe)

    public class Test {
         private static volatile Test test;
         private Test(){}
         public static Test getTest() {
            if(test == null) {
                synchronized(Test.class) {
                    if(test == null){test = new Test();
                }
            }
         }

        return test;
    }


3- Bill Pugh Singleton with Holder Pattern (Preferably the best one)

    public class Test {

        private Test(){}

        private static class TestHolder{
            private static final Test test = new Test();
        }

        public static Test getInstance(){
            return TestHolder.test;
        }
    }

4- enum singleton
      public enum MySingleton {
        INSTANCE;
    private MySingleton() {
        System.out.println("Here");
    }
}
7
Dheeraj Sachan

Ecco come implementare un semplice singleton:

public class Singleton {
    // It must be static and final to prevent later modification
    private static final Singleton INSTANCE = new Singleton();
    /** The constructor must be private to prevent external instantiation */ 
    private Singleton(){}
    /** The public static method allowing to get the instance */
    public static Singleton getInstance() {
        return INSTANCE;
    }
}

Ecco come creare correttamente il tuo singleton:

public class Singleton {
    // The constructor must be private to prevent external instantiation   
    private Singleton(){}
    /** The public static method allowing to get the instance */
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
    /** 
     * The static inner class responsible for creating your instance only on demand,
     * because the static fields of a class are only initialized when the class
     * is explicitly called and a class initialization is synchronized such that only 
     * one thread can perform it, this rule is also applicable to inner static class
     * So here INSTANCE will be created only when SingletonHolder.INSTANCE 
     * will be called
     */
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
}
4
Nicolas Filotto

Hai bisogno di double-checking idiom se hai bisogno di caricare la variabile di istanza di una classe pigramente. Se è necessario caricare una variabile statica o un singleton pigramente, è necessario l'inizializzazione su richiesta titolare idioma. 

Inoltre, se il singleton deve essere seriliazbile, tutti gli altri campi devono essere transitori e il metodo readResolve () deve essere implementato per mantenere invariato l'oggetto singleton. In caso contrario, ogni volta che l'oggetto viene deserializzato, verrà creata una nuova istanza dell'oggetto. Quello che readResolve () fa è sostituire il nuovo oggetto letto da readObject (), che ha costretto il nuovo oggetto a essere garbage collection in quanto non vi è alcuna variabile che si riferisca ad esso.

public static final INSTANCE == ....
private Object readResolve() {
  return INSTANCE; // original singleton instance.
} 
3
Onur

Vari modi per creare oggetti singleton:

  1. Secondo Joshua Bloch - Enum sarebbe il migliore.

  2. puoi anche usare il doppio check.

  3. È possibile utilizzare anche la classe statica interna.

3
Shailendra Singh

Enum singleton

Il modo più semplice per implementare un Singleton protetto da thread è l'utilizzo di un Enum

public enum SingletonEnum {
  INSTANCE;
  public void doSomething(){
    System.out.println("This is a singleton");
  }
}

Questo codice funziona dall'introduzione di Enum in Java 1.5

Doppio bloccaggio controllato

Se si desidera codificare un singleton "classico" che funziona in un ambiente con multithreading (a partire da Java 1.5), si dovrebbe usare questo.

public class Singleton {

  private static volatile Singleton instance = null;

  private Singleton() {
  }

  public static Singleton getInstance() {
    if (instance == null) {
      synchronized (Singleton.class){
        if (instance == null) {
          instance = new Singleton();
        }
      }
    }
    return instance ;
  }
}

Questo non è thread-safe prima di 1.5 perché l'implementazione della parola chiave volatile era diversa.

Caricamento iniziale Singleton (funziona anche prima di Java 1.5)

Questa implementazione crea un'istanza del singleton quando la classe viene caricata e fornisce la sicurezza del thread.

public class Singleton {

  private static final Singleton instance = new Singleton();

  private Singleton() {
  }

  public static Singleton getInstance() {
    return instance;
  }

  public void doSomething(){
    System.out.println("This is a singleton");
  }

}
3
Dan Moldovan

Per JSE 5.0 e versioni successive adottare l'approccio Enum, altrimenti utilizzare l'approccio statico del supporto singleton ((un approccio di caricamento lento descritto da Bill Pugh) .L'ultima soluzione è anche thread-safe senza richiedere costrutti di linguaggio speciali (cioè volatile o sincronizzato).

2
raoadnan

Un altro argomento spesso usato contro Singletons sono i loro problemi di testabilità. I single non sono facilmente ragguardabili a scopo di test. Se questo risulta essere un problema, mi piace apportare la seguente leggera modifica:

public class SingletonImpl {

    private static SingletonImpl instance;

    public static SingletonImpl getInstance() {
        if (instance == null) {
            instance = new SingletonImpl();
        }
        return instance;
    }

    public static void setInstance(SingletonImpl impl) {
        instance = impl;
    }

    public void a() {
        System.out.println("Default Method");
    }
}

Il metodo setInstance aggiunto consente di impostare un'implementazione mockup della classe singleton durante il test:

public class SingletonMock extends SingletonImpl {

    @Override
    public void a() {
        System.out.println("Mock Method");
    }

}

Questo funziona anche con i primi approcci di inizializzazione:

public class SingletonImpl {

    private static final SingletonImpl instance = new SingletonImpl();

    private static SingletonImpl alt;

    public static void setInstance(SingletonImpl inst) {
        alt = inst;
    }

    public static SingletonImpl getInstance() {
        if (alt != null) {
            return alt;
        }
        return instance;
    }

    public void a() {
        System.out.println("Default Method");
    }
}

public class SingletonMock extends SingletonImpl {

    @Override
    public void a() {
        System.out.println("Mock Method");
    }

}

Questo ha l'inconveniente di esporre questa funzionalità anche alla normale applicazione. Altri sviluppatori che lavorano su quel codice potrebbero essere tentati di usare il metodo theetInstance per alterare una funzione specifica e quindi modificare l'intero comportamento dell'applicazione, quindi questo metodo dovrebbe contenere almeno un buon avvertimento nel suo javadoc.

Tuttavia, per la possibilità di test di simulazione (quando necessario), questa esposizione di codice potrebbe essere un prezzo accettabile da pagare.

2
user3792852

la più semplice classe singleton

public class Singleton {
  private static Singleton singleInstance = new Singleton();
  private Singleton() {}
  public static Singleton getSingleInstance() {
    return singleInstance;
  }
}
1
rohan kamat

Continuo a pensare dopo Java 1.5, enum è la migliore implementazione disponibile singleton disponibile in quanto garantisce anche che anche negli ambienti multi-threaded - viene creata solo un'istanza.

public enum Singleton{ INSTANCE; }

e hai finito !!!

0
shikjohari

Dai un'occhiata a questo post.

Esempi di pattern di progettazione GoF nelle librerie principali di Java

Dalla sezione "Singleton" della risposta migliore,

Singleton (riconoscibile dai metodi creazionali che restituiscono la stessa istanza (solitamente di per sé) ogni volta)

  • Java.lang.Runtime # getRuntime ()
  • Java.awt.Desktop # getDesktop ()
  • Java.lang.System # getSecurityManager ()

Puoi anche imparare l'esempio di Singleton dalle stesse classi native di Java.

0
phi

Il miglior modello singleton che abbia mai visto utilizza l'interfaccia del fornitore.

  • È generico e riutilizzabile
  • Supporta l'inizializzazione pigra
  • È sincronizzato solo fino a quando non è stato inizializzato, quindi il fornitore di blocco viene sostituito con un fornitore non bloccante.

Vedi sotto:

public class Singleton<T> implements Supplier<T> {

    private boolean initialized;
    private Supplier<T> singletonSupplier;

    public Singleton(T singletonValue) {
        this.singletonSupplier = () -> singletonValue;
    }

    public Singleton(Supplier<T> supplier) {
        this.singletonSupplier = () -> {
            // The initial supplier is temporary; it will be replaced after initialization
            synchronized (supplier) {
                if (!initialized) {
                    T singletonValue = supplier.get();
                    // Now that the singleton value has been initialized,
                    // replace the blocking supplier with a non-blocking supplier
                    singletonSupplier = () -> singletonValue;
                    initialized = true;
                }
                return singletonSupplier.get();
            }
        };
    }

    @Override
    public T get() {
        return singletonSupplier.get();
    }
}
0
user1024314