Singleton con Argomenti in Java


142

Stavo leggendo l'articolo di Singleton su Wikipedia e mi sono imbattuto in questo esempio:

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 { 
        private static final Singleton INSTANCE = new Singleton();
    }

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

Anche se mi piace molto il modo in cui questo Singleton si comporta, non riesco a vedere come adattarlo per incorporare argomenti nel costruttore. Qual è il modo preferito per farlo in Java? Dovrei fare qualcosa del genere?

public class Singleton
{
    private static Singleton singleton = null;  
    private final int x;

    private Singleton(int x) {
        this.x = x;
    }

    public synchronized static Singleton getInstance(int x) {
        if(singleton == null) singleton = new Singleton(x);
        return singleton;
    }
}

Grazie!


Modifica: Penso di aver iniziato una tempesta di polemiche con il mio desiderio di usare Singleton. Lasciami spiegare la mia motivazione e spero che qualcuno possa suggerire un'idea migliore. Sto usando un framework di grid computing per eseguire compiti in parallelo. In generale, ho qualcosa del genere:

// AbstractTask implements Serializable
public class Task extends AbstractTask
{
    private final ReferenceToReallyBigObject object;

    public Task(ReferenceToReallyBigObject object)
    {
        this.object = object;
    }

    public void run()
    {
        // Do some stuff with the object (which is immutable).
    }
}

Quello che succede è che anche se ho semplicemente passato un riferimento ai miei dati a tutte le attività, quando le attività sono serializzate, i dati vengono copiati più e più volte. Quello che voglio fare è condividere l'oggetto tra tutte le attività. Naturalmente, potrei modificare la classe in questo modo:

// AbstractTask implements Serializable
public class Task extends AbstractTask
{
    private static ReferenceToReallyBigObject object = null;

    private final String filePath;

    public Task(String filePath)
    {
        this.filePath = filePath;
    }

    public void run()
    {
        synchronized(this)
        {
            if(object == null)
            {
                ObjectReader reader = new ObjectReader(filePath);
                object = reader.read();
            }
        }

        // Do some stuff with the object (which is immutable).
    }
}

Come puoi vedere, anche qui ho il problema che passare un percorso di file diverso non significa nulla dopo che il primo è passato. Ecco perché mi piace l'idea di un negozio che è stato pubblicato nelle risposte. Comunque, piuttosto che includere la logica per caricare il file nel metodo run, volevo astrarre questa logica in una classe Singleton. Non fornirò ancora un altro esempio, ma spero che tu abbia avuto l'idea. Per favore, fammi ascoltare le tue idee per un modo più elegante di realizzare ciò che sto cercando di fare. Grazie ancora!


1
Il modello di fabbrica è quello che vuoi. Idealmente, le attività della griglia dovrebbero essere completamente indipendenti da qualsiasi altra cosa e ricevere tutti i dati necessari per eseguire e restituire i risultati. Tuttavia, questa non è sempre la soluzione più fattibile, quindi serializzare i dati in un file non è una cattiva idea. Penso che tutta la faccenda del singleton sia un po 'un'aringa rossa; non vuoi un singleton.
oxbow_lakes,

2
È abbastanza spiacevole che tu abbia usato il termine Singleton che viene fornito con un tale bagaglio. Il termine appropriato per questo modello è in realtà Interning. Il interning è un metodo per garantire che i valori astratti siano rappresentati da una sola istanza. Lo string interning è l'uso più comune: en.wikipedia.org/wiki/String_intern_pool.
notnoop,

Potresti dare un'occhiata a Terracotta. Mantiene l'identità dell'oggetto attraverso il cluster. Quando si invia un riferimento a dati già presenti nel cluster, non viene nuovamente serializzato.
Taylor Gautier,

21
Mettendo da parte la questione se il modello singleton debba mai essere usato, noterei che quasi ogni risposta qui sembra presumere che lo scopo di fornire un argomento sia quello di consentire la creazione di "singleton multipli" che si distinguono per il valore di detto parametro. Ma un altro possibile scopo è quello di fornire l' accesso a un oggetto esterno che è l' unico oggetto del suo genere che l' istanza univoca della classe singleton avrà mai bisogno. Quindi dobbiamo distinguere un parametro fornito per tale accesso da un parametro destinato a creare "più istanze singleton".
Carl

2
Un altro scenario per un "singleton con parametri": un'applicazione web che costruirà il suo singleton immutabile unico basato sulle informazioni fornite con la primissima richiesta (thread). Il dominio della richiesta potrebbe determinare, ad esempio, alcuni comportamenti di singleton
fustaki

Risposte:


171

Metterò in chiaro il mio punto: un singleton con parametri non è un singleton .

Un singleton, per definizione, è un oggetto che si desidera creare un'istanza non più di una volta. Se stai provando a fornire parametri al costruttore, qual è il punto del singleton?

Hai due opzioni. Se si desidera inizializzare il singleton con alcuni dati, è possibile caricarlo con i dati dopo l'istanza , in questo modo:

SingletonObj singleton = SingletonObj.getInstance();
singleton.init(paramA, paramB); // init the object with data

Se l'operazione che esegue il tuo singleton è ricorrente e con parametri diversi ogni volta, potresti anche passare i parametri al metodo principale in esecuzione:

SingletonObj singleton = SingletonObj.getInstance();
singleton.doSomething(paramA, paramB); // pass parameters on execution

In ogni caso, l'istanza sarà sempre senza parametri. Altrimenti il ​​tuo singleton non è un singleton.


1
+1 Ecco come lo farei probabilmente durante la programmazione. In C #, però, userei solo le proprietà. Java, probabilmente così.
Zack,

131
scusa, non è vero. ci sono situazioni in cui devi passare parametri creati dinamicamente che rimangono gli stessi per il runtime dell'applicazione hole. quindi non puoi usare una costante all'interno del singleton ma devi passare quella costante quando viene creata. dopo aver passato una volta è la stessa costante per il tempo di buca. un setter non farà il lavoro se hai bisogno di quella costante specifica all'interno del costruttore.
masi,

1
@masi, come dice l'autore - non è un singleton. Se è necessario passare dinamicamente una costante, potrebbe essere necessario creare molte di queste classi con costanti diverse. Quindi, non ha senso singleton.
Dmitry Zaytsev,

53
Se hai solo bisogno di un'istanza di una classe per l'intera vita di un'applicazione, ma devi fornire a quell'istanza un valore al momento del lancio, perché non è più un singleton?
Oscar

4
"Se stai provando a fornire parametri al costruttore, qual è il punto del singleton?" - Si potrebbe anche dire: "Se si rende l'intera applicazione singola istanza, qual è il punto degli argomenti della riga di comando?", E la risposta è che ha molto senso. Si potrebbe ora dire che questo è abbastanza diverso da una classe singleton, tranne se la classe è in realtà la classe Main che riceve args [] dal metodo principale - quindi è anche la stessa cosa. L'argomento finale, che potrebbe essere valido, è che si tratta di una situazione abbastanza eccezionale.
Presidente Dreamspace il

41

Penso che tu abbia bisogno di qualcosa come una fabbrica per avere oggetti con vari parametri istanziati e riutilizzati. Potrebbe essere implementato usando un parametro sincronizzato HashMapo ConcurrentHashMapmappato ( Integerun esempio) alla classe parametrizzabile "singleton".

Anche se potresti arrivare al punto in cui dovresti usare invece classi regolari, non singleton (ad esempio che richiedono 10.000 singleton diversamente parametrizzati).

Ecco un esempio per tale negozio:

public final class UsefulObjFactory {

    private static Map<Integer, UsefulObj> store =
        new HashMap<Integer, UsefulObj>();

    public static final class UsefulObj {
        private UsefulObj(int parameter) {
            // init
        }
        public void someUsefulMethod() {
            // some useful operation
        }
    }

    public static UsefulObj get(int parameter) {
        synchronized (store) {
            UsefulObj result = store.get(parameter);
            if (result == null) {
                result = new UsefulObj(parameter);
                store.put(parameter, result);
            }
            return result;
        }
    }
}

Per spingerlo ulteriormente, i Java enumpossono anche essere considerati (o usati come) singleton parametrizzati, pur consentendo solo un numero fisso di varianti statiche.

Tuttavia, se è necessaria una soluzione distribuita 1 , prendere in considerazione una soluzione di memorizzazione nella cache laterale. Ad esempio: EHCache, Terracotta, ecc.

1 nel senso di estendere più VM su probabilmente più computer.


Sì, questo è esattamente ciò di cui ho bisogno. Grazie mille! Sono d'accordo che il modo in cui gestivo gli argomenti nel mio esempio non aveva molto senso, ma non ci ho pensato. Vedi la mia spiegazione nei commenti della risposta di oxbow_lakes.

1
Questo NON è un singleton; ora ne hai più di uno. LOL
oxbow_lakes,

@Scott: Suggerirei qualcosa di simile a ciò che Yuval ha suggerito di seguito. Ha un po 'più senso e hai un singleton "vero". modifica
Zack,

Spero che nessuno mi interessi a modificare i nomi nel codice; Posso immaginare che questo sia davvero confuso per i neofiti. Rollback se non sei d'accordo
oxbow_lakes,

Sì, potremmo chiamarli Multitron e raggiungere comunque lo stesso obiettivo che l'OP voleva in primo luogo IMHO.
Akarnokd,

22

È possibile aggiungere un metodo di inizializzazione configurabile per separare l'istanza da ottenere.

public class Singleton {
    private static Singleton singleton = null;
    private final int x;

    private Singleton(int x) {
        this.x = x;
    }

    public static Singleton getInstance() {
        if(singleton == null) {
            throw new AssertionError("You have to call init first");
        }

        return singleton;
    }

    public synchronized static Singleton init(int x) {
        if (singleton != null)
        {
            // in my opinion this is optional, but for the purists it ensures
            // that you only ever get the same instance when you call getInstance
            throw new AssertionError("You already initialized me");
        }

        singleton = new Singleton(x);
        return singleton;
    }

}

Quindi puoi chiamare Singleton.init(123)una volta per configurarlo, ad esempio all'avvio dell'app.


13

Puoi anche usare il modello Builder se vuoi mostrare che alcuni parametri sono obbligatori.

    public enum EnumSingleton {

    INSTANCE;

    private String name; // Mandatory
    private Double age = null; // Not Mandatory

    private void build(SingletonBuilder builder) {
        this.name = builder.name;
        this.age = builder.age;
    }

    // Static getter
    public static EnumSingleton getSingleton() {
        return INSTANCE;
    }

    public void print() {
        System.out.println("Name "+name + ", age: "+age);
    }


    public static class SingletonBuilder {

        private final String name; // Mandatory
        private Double age = null; // Not Mandatory

        private SingletonBuilder(){
          name = null;
        }

        SingletonBuilder(String name) {
            this.name = name;
        }

        public SingletonBuilder age(double age) {
            this.age = age;
            return this;
        }

        public void build(){
            EnumSingleton.INSTANCE.build(this);
        }

    }


}

Quindi è possibile creare / creare un'istanza / parametrizzare come segue:

public static void main(String[] args) {
    new EnumSingleton.SingletonBuilder("nico").age(41).build();
    EnumSingleton.getSingleton().print();
}

6

L' istruzione " Un singleton con parametri non è un singleton " non è completamente corretta . Dobbiamo analizzare questo dal punto di vista dell'applicazione piuttosto che dal punto di vista del codice.

Costruiamo la classe singleton per creare una singola istanza di un oggetto in un'unica esecuzione dell'applicazione. Avendo un costruttore con parametro, è possibile creare flessibilità nel codice per modificare alcuni attributi dell'oggetto singleton ogni volta che si esegue l'applicazione. Questa non è una violazione del modello Singleton. Sembra una violazione se vedi questo dal punto di vista del codice.

I pattern di progettazione sono lì per aiutarci a scrivere codice flessibile ed estensibile, non per impedirci di scrivere un buon codice.


12
Questa non è una risposta alla domanda del PO, questo dovrebbe essere un commento.
Thierry J.,

5

Utilizzare getter e setter per impostare la variabile e rendere privato il costruttore predefinito. Quindi utilizzare:

Singleton.getInstance().setX(value);

1
Non capisco perché questo è stato votato verso il basso .. È una risposta valida perbh. : /
Zack,

13
Perché è una risposta spazzatura. Ad esempio, immagina un sistema in cui il nome utente e la password iniziali per l'amministratore iniziale sono argomenti del costruttore. Ora, se lo faccio diventare un singleton e faccio come dici tu, ottengo getter e setter per l'amministratore, che non è proprio quello che vuoi. Quindi, sebbene in alcuni casi la tua opzione possa essere valida, in realtà non risponde al caso generale che era la domanda. (sì, sto lavorando sul sistema che ho descritto e no, non avrei usato uno schema singleton se non fosse per il fatto che il compito dice "usa uno schema singleton qui")
Jasper,

5

Sorpreso che nessuno abbia menzionato il modo in cui un logger viene creato / recuperato. Ad esempio, di seguito mostra come viene recuperato il logger Log4J .

// Retrieve a logger named according to the value of the name parameter. If the named logger already exists, then the existing instance will be returned. Otherwise, a new instance is created.
public static Logger getLogger(String name)

Esistono alcuni livelli di riferimenti indiretti, ma la parte fondamentale è sotto il metodo che praticamente dice tutto su come funziona. Utilizza una tabella hash per archiviare i logger esistenti e la chiave deriva dal nome. Se il logger non esiste per un nome, utilizza una factory per creare il logger e quindi lo aggiunge alla tabella hash.

69   Hashtable ht;
...
258  public
259  Logger getLogger(String name, LoggerFactory factory) {
260    //System.out.println("getInstance("+name+") called.");
261    CategoryKey key = new CategoryKey(name);
262    // Synchronize to prevent write conflicts. Read conflicts (in
263    // getChainedLevel method) are possible only if variable
264    // assignments are non-atomic.
265    Logger logger;
266
267    synchronized(ht) {
268      Object o = ht.get(key);
269      if(o == null) {
270        logger = factory.makeNewLoggerInstance(name);
271        logger.setHierarchy(this);
272        ht.put(key, logger);
273        updateParents(logger);
274        return logger;
275      } else if(o instanceof Logger) {
276        return (Logger) o;
277      } 
...

4

Modifica del modello Singleton che utilizza l'inizializzazione di Bill Pugh su idioma del titolare della domanda . Questo è thread-safe senza il sovraccarico di costrutti linguistici specializzati (ovvero volatili o sincronizzati):

public final class RInterfaceHL {

    /**
     * Private constructor prevents instantiation from other classes.
     */
    private RInterfaceHL() { }

    /**
     * R REPL (read-evaluate-parse loop) handler.
     */
    private static RMainLoopCallbacks rloopHandler = null;

    /**
     * SingletonHolder is loaded, and the static initializer executed, 
     * on the first execution of Singleton.getInstance() or the first 
     * access to SingletonHolder.INSTANCE, not before.
     */
    private static final class SingletonHolder {

        /**
         * Singleton instance, with static initializer.
         */
        private static final RInterfaceHL INSTANCE = initRInterfaceHL();

        /**
         * Initialize RInterfaceHL singleton instance using rLoopHandler from
         * outer class.
         * 
         * @return RInterfaceHL instance
         */
        private static RInterfaceHL initRInterfaceHL() {
            try {
                return new RInterfaceHL(rloopHandler);
            } catch (REngineException e) {
                // a static initializer cannot throw exceptions
                // but it can throw an ExceptionInInitializerError
                throw new ExceptionInInitializerError(e);
            }
        }

        /**
         * Prevent instantiation.
         */
        private SingletonHolder() {
        }

        /**
         * Get singleton RInterfaceHL.
         * 
         * @return RInterfaceHL singleton.
         */
        public static RInterfaceHL getInstance() {
            return SingletonHolder.INSTANCE;
        }

    }

    /**
     * Return the singleton instance of RInterfaceHL. Only the first call to
     * this will establish the rloopHandler.
     * 
     * @param rloopHandler
     *            R REPL handler supplied by client.
     * @return RInterfaceHL singleton instance
     * @throws REngineException
     *             if REngine cannot be created
     */
    public static RInterfaceHL getInstance(RMainLoopCallbacks rloopHandler)
            throws REngineException {
        RInterfaceHL.rloopHandler = rloopHandler;

        RInterfaceHL instance = null;

        try {
            instance = SingletonHolder.getInstance();
        } catch (ExceptionInInitializerError e) {

            // rethrow exception that occurred in the initializer
            // so our caller can deal with it
            Throwable exceptionInInit = e.getCause();
            throw new REngineException(null, exceptionInInit.getMessage());
        }

        return instance;
    }

    /**
     * org.rosuda.REngine.REngine high level R interface.
     */
    private REngine rosudaEngine = null;

    /**
     * Construct new RInterfaceHL. Only ever gets called once by
     * {@link SingletonHolder.initRInterfaceHL}.
     * 
     * @param rloopHandler
     *            R REPL handler supplied by client.
     * @throws REngineException
     *             if R cannot be loaded.
     */
    private RInterfaceHL(RMainLoopCallbacks rloopHandler)
            throws REngineException {

        // tell Rengine code not to die if it can't
        // load the JRI native DLLs. This allows
        // us to catch the UnsatisfiedLinkError
        // ourselves
        System.setProperty("jri.ignore.ule", "yes");

        rosudaEngine = new JRIEngine(new String[] { "--no-save" }, rloopHandler);
    }
}

Penso che sarebbe una buona idea finally { RInterfaceHL.rloopHandler = null; }in getInstance, perché quel riferimento statico può causare una perdita di memoria, se non stiamo attenti. Nel tuo caso sembra che non sia un problema, ma potrei immaginare uno scenario in cui l'oggetto passato è grande e utilizzato solo da RInterfaceHLctor per ottenere alcuni valori e non per mantenerne un riferimento.
TWiStErRob

Idea: return SingletonHolder.INSTANCEfunzionerebbe altrettanto bene getInstance. Non penso che ci sia bisogno di incapsulamento qui, perché la classe esterna conosce già le interiora della classe interna, sono strettamente accoppiate: sa che ha rloopHandlerbisogno di init prima di chiamare. Anche il costruttore privato non ha alcun effetto, perché i contenuti privati ​​della classe interna sono semplicemente disponibili per la classe esterna.
TWiStErRob

1
Il collegamento è interrotto. Ti riferivi a en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom ?
Jorge Lavín,

3

La ragione per cui non riesci a capire come realizzare ciò che stai cercando di fare è probabilmente che ciò che stai cercando di fare non ha davvero senso. Vuoi chiamare getInstance(x)con argomenti diversi, ma restituire sempre lo stesso oggetto? Che comportamento vuoi quando chiami getInstance(2)e poi getInstance(5)?

Se vuoi lo stesso oggetto ma per il suo valore interno è diverso, il che è l'unico modo in cui è ancora un singleton, allora non devi preoccuparti del costruttore; hai appena impostato il valore getInstance()sulla via d'uscita dell'oggetto. Naturalmente, capisci che tutti gli altri riferimenti al singleton ora hanno un valore interno diverso.

Se vuoi getInstance(2)e getInstance(5)restituire oggetti diversi, d'altra parte, non stai usando il modello Singleton, stai usando il modello Factory.


3

Nel tuo esempio non stai usando un singleton. Si noti che se si esegue quanto segue (presupponendo che Singleton.getInstance fosse effettivamente statico):

Singleton obj1 = Singleton.getInstance(3);
Singleton obj2 = Singleton.getInstance(4);

Quindi i valori di obj2.x sono 3, non 4. Se hai bisogno di farlo, rendilo una classe semplice. Se il numero di valori è piccolo e fisso, puoi considerare di utilizzare un enum. Se hai problemi con la generazione eccessiva di oggetti (che di solito non è il caso), puoi prendere in considerazione i valori di memorizzazione nella cache (e controllare le fonti o ottenere aiuto con ciò, poiché è ovvio come costruire cache senza il pericolo di perdite di memoria).

Potresti anche voler leggere questo articolo in quanto i singoli possono essere facilmente abusati.


3

Un altro motivo per cui i Singleton sono un anti-pattern è che se scritti secondo le raccomandazioni, con un costruttore privato, sono molto difficili da sottoclassare e configurare per l'uso in determinati test unitari. Ad esempio, sarebbe necessario per mantenere il codice legacy.


3

Se si desidera creare una classe Singleton che funge da contesto, un buon modo è disporre di un file di configurazione e leggere i parametri dal file all'interno di instance ().

Se i parametri che alimentano la classe Singleton vengono acquisiti in modo dinamico durante l'esecuzione del programma, utilizzare semplicemente una HashMap statica che memorizza istanze diverse nella classe Singleton per assicurarsi che per ciascun parametro venga creata solo un'istanza.


1

Questo non è proprio un singleton, ma potrebbe essere qualcosa che potrebbe risolvere il tuo problema.

public class KamilManager {

  private static KamilManager sharedInstance;

  /**
   * This method cannot be called before calling KamilManager constructor or else
   * it will bomb out.
   * @return
   */
  public static KamilManager getInstanceAfterInitialized() {
    if(sharedInstance == null)
        throw new RuntimeException("You must instantiate KamilManager once, before calling this method");

    return sharedInstance;
}

  public KamilManager(Context context, KamilConfig KamilConfig) {
    //Set whatever you need to set here then call:
  s  haredInstance = this;
  }
}

1

Se prendiamo il problema come "come creare singleton con stato", non è necessario passare lo stato come parametro del costruttore. Sono d'accordo con i post che inizializzano gli stati o usando il metodo set dopo aver ottenuto l'istanza singleton.

Un'altra domanda è: è bello avere singleton con lo stato?


1

Non potremmo fare qualcosa del genere:

public class Singleton {

    private int x;

    // 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 { 
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance(int x) {
        Singleton instance = SingletonHolder.INSTANCE;
        instance.x = x;
        return instance;
    }
}

1

Nonostante ciò che alcuni potrebbero affermare, ecco un singleton con parametri nel costruttore

public class Singleton {

    private static String aParameterStored;

    private static final Singleton instance = new Singleton("Param to set");

    private Singleton() {
        // do nothing
    }

    private Singleton(String param) {
        aParameterStored = param;
    }

    public static Singleton getInstance() {
        return instance;
    }

    /*
     * ... stuff you would like the singleton do
     */
}

Lo schema singleton dice:

  • assicurarsi che esista una sola istanza della classe singleton
  • fornire accesso globale a tale istanza.

che sono rispettati con questo esempio.

Perché non impostare direttamente la proprietà? È un caso da manuale per mostrare come possiamo ottenere un singleton con costruttore con parametro ma potrebbe essere utile in alcune situazioni. Ad esempio nei casi di ereditarietà per forzare il singleton a impostare alcune proprietà della superclasse.


0

Ho paura di postare questo come una risposta, ma non capisco perché nessuno ci pensi, forse questa risposta è stata anche data già non l'ho capito.

public class example  {
    private volatile static example instance;

    private String string;
    private int iInt = -1; //any number you know you don't want to use here

  private example() {

    //In case someone uses the private method to create a new Instance
    if (instance != null){
      throw new RuntimeException("Use getInstance() method to get the single instance of this class.");
    }
  }

  public synchronized static example getIsntance(){
    if(instance == null){
      instance = new example();
    }
    return instance;
  }

public void methodDoingWork(){
    if(checkInit()){
      //DoSome
    }
  }

  private boolean checkInit(){
    boolean filled = (this.string != null) && (this.iInt != -1);
    return filled;
  }

  public void setString(String string) {
    if(this.string == null){
      this.string = string;
    }else{
      throw new RuntimeException("You try to override an already setValue"); 
    }
  }

  public void setiInt(int iInt) {
    if(this.iInt == -1){
      this.iInt = iInt;
    }else{
      throw new RuntimeException("You try to override an already setValue");
    }
  }
}

Dato che getInstance()restituisce la stessa istanza ogni volta, penso che potrebbe funzionare. Se questo è sbagliato per molto lo eliminerò, sono solo interessato a questo argomento.


-1

Penso che questo sia un problema comune. Separare l '"inizializzazione" del singleton dalla "get" del singleton potrebbe funzionare (in questo esempio viene utilizzata una variante del blocco con doppio controllo).

public class MySingleton {

    private static volatile MySingleton INSTANCE;

    @SuppressWarnings("UnusedAssignment")
    public static void initialize(
            final SomeDependency someDependency) {

        MySingleton result = INSTANCE;

        if (result != null) {
            throw new IllegalStateException("The singleton has already "
                    + "been initialized.");
        }

        synchronized (MySingleton.class) {
            result = INSTANCE;

            if (result == null) {
                INSTANCE = result = new MySingleton(someDependency);
            } 
        }
    }

    public static MySingleton get() {
        MySingleton  result = INSTANCE;

        if (result == null) {
            throw new IllegalStateException("The singleton has not been "
                    + "initialized. You must call initialize(...) before "
                    + "calling get()");
        }

       return result;
    }

    ...
}

Potrei sempre restituire "risultato" nel metodo di inizializzazione e suppongo.
Michael Andrews,

-2

Singleton è, ovviamente, un "anti-pattern" (assumendo una definizione di statico con stato variabile).

Se si desidera un set fisso di oggetti valore immutabili, allora gli enum sono la strada da percorrere. Per un set di valori ampio, possibilmente aperto, è possibile utilizzare un repository in qualche forma, generalmente basato su Mapun'implementazione. Naturalmente, quando hai a che fare con la statica fai attenzione al threading (o sincronizza sufficientemente ampiamente o usa uno dei ConcurrentMapdue controllando che un altro thread non ti abbia battuto o usi qualche forma di futuro).


4
Solo un anti-pattern se usato in modo errato, sebbene questa sia la definizione di anti-pattern. Solo perché li hai visti dove non appartenevano al passato non significa che non abbiano un posto.
geowa4,

L'uso corretto di un singleton è dimostrare codice incompetente.
Tom Hawtin - tackline il

-6

I singleton sono generalmente considerati anti-pattern e non dovrebbero essere usati. Non rendono il codice facile da testare.

Un singleton con una discussione non ha comunque senso: cosa accadrebbe se scrivessi:

Singleton s = SingletonHolder.getInstance(1);
Singleton t = SingletonHolder.getInstance(2); //should probably throw IllegalStateException

Inoltre, il tuo singleton non è sicuro per i thread poiché più thread possono effettuare chiamate simultanee per creare getInstancepiù di un'istanza (possibilmente con valori diversi di x).


È abbastanza discutibile.
AlbertoPL,

1
Sì, è discutibile; da qui il mio uso della parola "generalmente". Penso che sia giusto dire che sono generalmente considerati una cattiva idea
oxbow_lakes,

È discutibile - alcune persone affermano che quelli che vengono chiamati "anti-schemi" si adattano alla definizione di schemi, è solo che sono schemi cattivi.
Tom Hawtin - tackline il

Capisco che sono cattivi. Sto facendo il calcolo distribuito e ho bisogno di condividere un oggetto tra più attività. Piuttosto che inizializzare deterministicamente una variabile statica, vorrei astrarre la logica in un Singleton. Immagino di poter sincronizzare getInstance. Funzionerebbe? Quello che devo fare è caricare un file una volta per molte attività e solo dopo che la prima attività è stata inviata. (Non voglio che i miei dati siano serializzati.) Ho pensato di rendere il mio AbstractFileReader un argomento per il metodo getInstance al fine di rendere il Singleton più flessibile. Apprezzo il tuo contributo.

Penso che potresti fraintendere cosa significhi "distribuito"? Esistono altri modi per ottenere ciò che desideri: hai considerato l'iniezione di dipendenza? O JNDI?
oxbow_lakes,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.