Singleton con argumentos en Java

142

Estaba leyendo el artículo de Singleton en Wikipedia y me encontré con este ejemplo:

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;
    }
}

Aunque realmente me gusta la forma en que se comporta este Singleton, no puedo ver cómo adaptarlo para incorporar argumentos al constructor. ¿Cuál es la forma preferida de hacer esto en Java? ¿Tendría que hacer algo como esto?

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;
    }
}

¡Gracias!


Editar: Creo que he comenzado una tormenta de controversias con mi deseo de usar Singleton. Déjame explicarte mi motivación y espero que alguien pueda sugerir una mejor idea. Estoy usando un marco de computación grid para ejecutar tareas en paralelo. En general, tengo algo como esto:

// 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).
    }
}

Lo que sucede es que a pesar de que simplemente paso una referencia a mis datos a todas las tareas, cuando las tareas se serializan, los datos se copian una y otra vez. Lo que quiero hacer es compartir el objeto entre todas las tareas. Naturalmente, podría modificar la clase así:

// 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).
    }
}

Como puede ver, incluso aquí tengo el problema de que pasar una ruta de archivo diferente no significa nada después de que se pasa la primera. Es por eso que me gusta la idea de una tienda que se publicó en las respuestas. De todos modos, en lugar de incluir la lógica para cargar el archivo en el método de ejecución, quería abstraer esta lógica en una clase Singleton. No daré otro ejemplo más, pero espero que entiendan la idea. Permítame escuchar sus ideas para una forma más elegante de lograr lo que estoy tratando de hacer. ¡Gracias de nuevo!


fuente
1
El patrón de fábrica es lo que quieres. Idealmente, las tareas de cuadrícula deberían ser completamente independientes de cualquier otra cosa y recibir todos los datos que necesitan para ejecutar y devolver sus resultados. Sin embargo, esta no es siempre la solución más factible, por lo que serializar los datos en un archivo no es una mala idea. Creo que todo el asunto de los singleton es un poco de arenque; No quieres un singleton.
oxbow_lakes
2
Es lamentable que haya utilizado el término Singleton que viene con dicho equipaje. El término apropiado para este patrón es Interning en realidad. Interning es un método para garantizar que los valores abstractos estén representados por una sola instancia. El internamiento de cadenas es el uso más común: en.wikipedia.org/wiki/String_intern_pool.
notnoop
Es posible que desee echar un vistazo a la terracota. Mantiene la identidad del objeto en todo el clúster. Cuando envía una referencia a datos que ya están en el clúster, no se vuelve a serializar.
Taylor Gautier
21
Dejando a un lado la cuestión de si alguna vez se debe usar el patrón de singleton, me gustaría señalar que casi todas las respuestas aquí parecen suponer que el propósito de proporcionar un argumento es permitir que se creen "singletons múltiples" que se distingan por el valor de dicho parámetro. Pero otro posible propósito es proporcionar acceso a un objeto externo que es el único objeto de este tipo que necesitará la instancia única de la clase singleton . Por lo tanto, debemos distinguir un parámetro proporcionado para dicho acceso de un parámetro destinado a crear "instancias de singleton múltiples".
Carl
2
Otro escenario para un "singleton con parámetros": una aplicación web que construirá su singleton inmutable único basado en la información que viene con la primera solicitud próxima (hilo). El dominio de la solicitud podría determinar el comportamiento de algunos solteros, por ejemplo
fustaki

Respuestas:

171

Dejaré mi punto muy claro: un singleton con parámetros no es un singleton .

Un singleton, por definición, es un objeto que desea instanciar no más de una vez. Si está tratando de alimentar parámetros al constructor, ¿cuál es el punto del singleton?

Tienes dos opciones. Si desea que su singleton se inicialice con algunos datos, puede cargarlo con datos después de la creación de instancias , de esta manera:

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

Si la operación que realiza su singleton es recurrente, y con diferentes parámetros cada vez, también podría pasar los parámetros al método principal que se está ejecutando:

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

En cualquier caso, la instanciación siempre será sin parámetros. De lo contrario, su singleton no es un singleton.

Yuval Adam
fuente
1
+1 Así es como probablemente lo haría al codificar. Sin embargo, en C #, solo usaría propiedades. Java, probablemente así.
Zack
131
Lo siento, eso no es cierto. hay situaciones en las que tiene que pasar parámetros creados dinámicamente que permanecen igual para el tiempo de ejecución de la aplicación de agujeros. así que no puedes usar una constante dentro del singleton pero tienes que pasar esa constante cuando se crea. después de pasar una vez, es la misma constante para el tiempo del hoyo. un setter no hará el trabajo si necesita esa constante específica dentro del constructor.
masi
1
@masi, como dice el autor, no es un singleton. Si necesita pasar dinámicamente constante, es posible que necesite crear muchas de esas clases con diferentes constantes. Entonces, no tiene sentido en singleton.
Dmitry Zaytsev
53
Si solo necesita una instancia de una clase para toda la vida útil de una aplicación, pero necesita proporcionar a esa instancia un valor en el momento del lanzamiento, ¿por qué ya no es un singleton?
Oscar
44
"Si está tratando de alimentar parámetros al constructor, ¿cuál es el punto del singleton?" - También se podría decir: "Si hace que toda su aplicación sea una instancia única, ¿cuál es el punto de los argumentos de la línea de comando?", Y la respuesta es que tiene mucho sentido. Ahora se podría decir que esto es bastante diferente de una clase singleton, excepto si la clase es en realidad la clase Main que recibe los args [] del método main, entonces es incluso lo mismo. El argumento final, que podría mantenerse, es que esta es una situación bastante excepcional.
Dreamspace President
41

Creo que necesitas algo como un fábrica para tener objetos con varios parámetros instanciados y reutilizados. Podría implementarse utilizando un parámetro sincronizado HashMapo ConcurrentHashMapmapeado (unInteger por ejemplo,) a su clase parametrizable 'singleton'.

Aunque puede llegar al punto en el que debería usar clases regulares, no singleton en su lugar (por ejemplo, necesita 10.000 singleton parametrizados de manera diferente).

Aquí hay un ejemplo para tal tienda:

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;
        }
    }
}

Para impulsarlo aún más, el Java enum s también pueden considerarse (o usarse como) singletons parametrizados, aunque solo permiten un número fijo de variantes estáticas.

Sin embargo, si necesita una solución distribuida 1 , considere alguna solución de almacenamiento en caché lateral. Por ejemplo: EHCache, terracota, etc.

1 en el sentido de abarcar múltiples máquinas virtuales en probablemente múltiples computadoras.

akarnokd
fuente
Sí, esto es exactamente lo que necesito. ¡Muchas gracias! Estoy de acuerdo en que la forma en que manejaba los argumentos en mi ejemplo no tenía mucho sentido, pero no pensé en esto. Vea mi explicación en los comentarios de la respuesta de oxbow_lakes.
1
Esto NO es un singleton; ahora tienes más de uno de ellos. LOL
oxbow_lakes
@ Scott: Sugeriría algo como lo que Yuval sugirió a continuación. Tiene un poco más de sentido y tienes un singleton 'verdadero'. edit
Zack
Espero que a nadie le importe editar los nombres en el código; Me imagino que esto es realmente confuso para los novatos. Rollback si no está de acuerdo
oxbow_lakes
Sí, podríamos llamarlos Multitron y seguir logrando el mismo objetivo que el OP quería en primer lugar en mi humilde opinión.
akarnokd
22

Puede agregar un método de inicialización configurable para separar la instanciación de la obtención.

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;
    }

}

Luego puede llamar Singleton.init(123)una vez para configurarlo, por ejemplo, en el inicio de su aplicación.

miguel
fuente
13

También puede usar el patrón Builder si desea mostrar que algunos parámetros son obligatorios.

    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);
        }

    }


}

Entonces podría crear / instanciar / parametrizarlo de la siguiente manera:

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

La declaración " Un singleton con parámetros no es un singleton " no es completamente correcta . Necesitamos analizar esto desde la perspectiva de la aplicación en lugar de desde la perspectiva del código.

Construimos una clase singleton para crear una sola instancia de un objeto en una ejecución de aplicación. Al tener un constructor con parámetro, puede generar flexibilidad en su código para cambiar algunos atributos de su objeto singleton cada vez que ejecute su aplicación. Esto no es una violación del patrón Singleton. Parece una violación si ve esto desde la perspectiva del código.

Los patrones de diseño están ahí para ayudarnos a escribir código flexible y extensible, no para impedirnos escribir un buen código.

Vinod Nalla
fuente
12
Esta no es una respuesta a la pregunta de OP, debería ser un comentario.
Thierry J.
5

Use getters y setters para establecer la variable y hacer que el constructor predeterminado sea privado. Luego use:

Singleton.getInstance().setX(value);
AlbertoPL
fuente
1
No entiendo por qué esto fue rechazado ... Es una respuesta válida tbh. : /
Zack
13
Porque es una respuesta basura. Por ejemplo, imagine un sistema donde el nombre de usuario y la contraseña iniciales para el administrador inicial son argumentos de constructor. Ahora, si hago de esto un singleton y hago lo que usted dice, obtengo getters y setters para el administrador, que no es exactamente lo que quiere. Entonces, aunque su opción puede ser válida en algunos casos, en realidad no responde al caso general que era la pregunta. (sí, estoy trabajando en el sistema que describí y no, no hubiera usado un patrón singleton si no fuera por el hecho de que la asignación dice "use un patrón singleton aquí")
Jasper
5

Sorprendido de que nadie mencionó cómo se crea / recupera un registrador. Por ejemplo, a continuación se muestra cómo se recupera el registrador 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)

Hay algunos niveles de indirecciones, pero la parte clave está debajo del método que prácticamente dice todo sobre cómo funciona. Utiliza una tabla hash para almacenar los registradores existentes y la clave se deriva del nombre. Si el registrador no existe para un nombre de pila, utiliza una fábrica para crear el registrador y luego lo agrega a la tabla 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      } 
...
wanghq
fuente
4

Modificación del patrón Singleton que utiliza la inicialización de Bill Pugh en el idioma del titular de la demanda . Esto es seguro para subprocesos sin la sobrecarga de construcciones de lenguaje especializado (es decir, volátil o sincronizado):

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);
    }
}
tekumara
fuente
Creo que sería una buena idea que finally { RInterfaceHL.rloopHandler = null; }en getInstance, debido a que la referencia estática puede causar una pérdida de memoria si no tenemos cuidado. En su caso, parece que no es un problema, pero podría imaginar un escenario en el que el objeto pasado es grande y solo lo utiliza RInterfaceHLctor para obtener algunos valores y no para mantener una referencia a él.
TWiStErRob
Idea: return SingletonHolder.INSTANCEfuncionaría igual de bien getInstance. No creo que sea necesario encapsular aquí, porque la clase externa ya conoce las entrañas de la clase interna, están estrechamente acopladas: sabe que rloopHandlernecesita init antes de llamar. Además, el constructor privado no tiene ningún efecto, porque las cosas privadas de la clase interna simplemente están disponibles para la clase externa.
TWiStErRob
1
El enlace está roto. ¿Se refería a en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom ?
Jorge Lavín
3

La razón por la que no puede entender cómo lograr lo que está tratando de hacer es probablemente porque lo que está tratando de hacer no tiene sentido. ¿Desea llamar getInstance(x)con diferentes argumentos, pero siempre devuelve el mismo objeto? ¿Qué comportamiento quieres cuando llamas getInstance(2)y luego getInstance(5)?

Si desea el mismo objeto pero su valor interno es diferente, que es la única forma en que sigue siendo un singleton, entonces no necesita preocuparse por el constructor; simplemente establece el valor en getInstance()la salida del objeto. Por supuesto, comprende que todas sus otras referencias al singleton ahora tienen un valor interno diferente.

Si desea getInstance(2)y getInstance(5)devolver diferentes objetos, por otro lado, no está utilizando el patrón Singleton, está utilizando el patrón Factory.

caos
fuente
3

En su ejemplo, no está utilizando un singleton. Tenga en cuenta que si hace lo siguiente (suponiendo que Singleton.getInstance sea realmente estático):

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

Entonces los valores de obj2.x son 3, no 4. Si necesita hacer esto, conviértalo en una clase simple. Si el número de valores es pequeño y fijo, puede considerar usar unenum . Si tiene problemas con la generación excesiva de objetos (que generalmente no es el caso), puede considerar los valores de almacenamiento en caché (y verificar las fuentes u obtener ayuda con eso, ya que es obvio cómo construir cachés sin el peligro de pérdidas de memoria).

También es posible que desee leer este artículo, ya que los singletons pueden ser utilizados en exceso fácilmente.

Kathy Van Stone
fuente
3

Otra razón por la que los Singletons son un antipatrón es que si se escriben de acuerdo con las recomendaciones, con un constructor privado, son muy difíciles de subclasificar y configurar para usar en ciertas pruebas unitarias. Sería necesario para mantener el código heredado, por ejemplo.

JosefB
fuente
3

Si desea crear una clase Singleton que sirva como contexto, una buena manera es tener un archivo de configuración y leer los parámetros del archivo dentro de la instancia ().

Si los parámetros que alimentan la clase Singleton se obtienen dinámicamente durante la ejecución de su programa, simplemente use un HashMap estático que almacene diferentes instancias en su clase Singleton para asegurarse de que para cada parámetro (s), solo se cree una instancia.

usuario3025839
fuente
1

Esto no es un singleton, pero puede ser algo que podría solucionar su 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;
  }
}
Kamilski81
fuente
1

Si tomamos el problema como "cómo hacer singleton con estado", entonces no es necesario pasar el estado como parámetro constructor. Estoy de acuerdo con las publicaciones que inicializan los estados o que usan el método set después de obtener la instancia singleton.

Otra pregunta es: ¿es bueno tener singleton con estado?

usuario3014901
fuente
1

¿No podríamos hacer algo como esto?

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;
    }
}
Ionut Negru
fuente
1

A pesar de lo que algunos pueden afirmar, aquí hay un singleton con parámetros en el constructor

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
     */
}

El patrón singleton dice:

  • asegúrese de que solo exista una instancia de la clase singleton
  • proporcionar acceso global a esa instancia.

que se respetan con este ejemplo.

¿Por qué no establecer directamente la propiedad? Es el caso del libro de texto para mostrar cómo podemos obtener un singleton que tenga un constructor con parámetro, pero podría ser útil en algunas situaciones. Por ejemplo, en casos de herencia para forzar al singleton a establecer algunas propiedades de superclase.

Zou
fuente
0

Tengo miedo de publicar esto como respuesta, pero no entiendo por qué nadie piensa en esto, tal vez esta respuesta también se dio ya que simplemente no la entendí.

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");
    }
  }
}

Como getInstance()devuelve la misma instancia cada vez, creo que esto podría funcionar. Si esto es incorrecto, lo eliminaré, solo estoy interesado en este tema.

HydroHeiperGen
fuente
-1

Creo que este es un problema común. La separación de la "inicialización" del singleton del "get" del singleton podría funcionar (este ejemplo utiliza una variación de bloqueo de doble verificación).

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;
    }

    ...
}
Michael Andrews
fuente
Supongo que siempre podría devolver "resultado" en el método de inicialización.
Michael Andrews
-2

Singleton es, por supuesto, un "antipatrón" (suponiendo una definición de un estado estático con estado variable).

Si desea un conjunto fijo de objetos de valor inmutable, entonces las enumeraciones son el camino a seguir. Para un conjunto de valores grande, posiblemente abierto, puede usar un Repositorio de alguna forma, generalmente basado en una Mapimplementación. Por supuesto, cuando se trata de estadísticas, tenga cuidado con los subprocesos (sincronice lo suficiente o use ConcurrentMapcomprobando que otro hilo no lo ha vencido o use alguna forma de futuros).

Tom Hawtin - tackline
fuente
44
Solo un antipatrón si se usa incorrectamente, aunque esa es la definición de un antipatrón. El hecho de que los haya visto donde no pertenecían en el pasado no significa que no tengan un lugar.
geowa4
El uso correcto de un singleton es demostrar código incompetente.
Tom Hawtin - tackline
-6

Los singletons generalmente se consideran antipatrones y no deben usarse. No hacen que el código sea fácil de probar.

Un singleton con un argumento no tiene sentido de todos modos, ¿qué pasaría si escribieras:

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

Su singleton tampoco es seguro para subprocesos, ya que varios subprocesos pueden realizar llamadas simultáneas para getInstanceque se cree más de una instancia (posiblemente con diferentes valores de x).

oxbow_lakes
fuente
Eso es bastante discutible.
AlbertoPL
1
Sí, es discutible; de ahí mi uso de la palabra "en general". Creo que es justo decir que, en general, se les considera una mala idea
oxbow_lakes
Es discutible: algunas personas afirman que los llamados "antipatrones" se ajustan a la definición de patrones, es solo que son patrones malos.
Tom Hawtin - tackline
Entiendo que son malos. Estoy haciendo computación distribuida y necesito compartir un objeto entre múltiples tareas. En lugar de inicializar determinísticamente una variable estática, me gustaría abstraer la lógica en un Singleton. Me imagino que podría hacer que getInstance esté sincronizado. ¿Funcionaría esto? Lo que necesito hacer es cargar un archivo una vez para muchas tareas, y solo después de que se haya enviado la primera tarea. (No quiero que mis datos se serialicen). Pensé que haría de mi AbstractFileReader un argumento para el método getInstance para hacer que Singleton sea más flexible. Valoro tu aporte.
Creo que puedes entender mal lo que significa "distribuido". Hay otras formas de lograr lo que desea: ¿ha considerado la inyección de dependencia? O JNDI?
oxbow_lakes