variable usar sirve que patron para metodos metodo estaticos cuando clase java oop singleton anti-patterns

usar - Singleton con argumentos en Java



singleton java (19)

Estaba leyendo el artículo de Singleton en Wikipedia y encontré 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 veo 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 comencé una tormenta de controversia con mi deseo de usar Singleton. Permítanme explicar mi motivación y espero que alguien pueda sugerir una mejor idea. Estoy usando un marco de trabajo de 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 aunque paso simplemente 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 pase el primero. 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 resumir esta lógica en una clase Singleton. No daré otro ejemplo más, pero espero que entiendas la idea. Permítanme escuchar sus ideas para una forma más elegante de lograr lo que estoy tratando de hacer. ¡Gracias de nuevo!


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

Construimos una clase singleton para crear una única instancia de un objeto en una aplicación ejecutada. 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 ejecuta su aplicación. Esto no es una violación del patrón de Singleton. Parece una violación si ves esto desde la perspectiva del código.

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


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


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 un caso de 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.


Creo que este es un problema común. Puede funcionar separar la "inicialización" del singleton del "get" del singleton (este ejemplo usa una variación del doble bloqueo verificado).

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


Creo que necesitas algo como una fábrica para tener objetos con varios parámetros instanciados y reutilizados. Podría implementarse utilizando un mapa sincronizado HashMap o ConcurrentHashMap un parámetro (un Integer para un ejemplo) a su clase parametrizable ''singleton''.

Aunque es posible que llegue al punto en el que deba utilizar clases normales, no únicas (por ejemplo, necesita 10.000 singleton parametrizados de forma 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, las enum Java también se pueden considerar (o usar 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, Terracotta, etc.

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


En su ejemplo, no está utilizando un singleton. Tenga en cuenta que si hace lo siguiente (suponiendo que Singleton.getInstance fuera en realidad 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 el uso de una enum . Si tiene problemas con la generación excesiva de objetos (que generalmente no es el caso), puede considerar almacenar en caché los valores (y consultar fuentes u obtener ayuda con eso, ya que es obvio cómo construir cachés sin el peligro de fugas de memoria).

También es posible que desee leer este artículo, ya que los singletons se pueden abusar fácilmente.


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


Haré 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 intenta alimentar parámetros al constructor, ¿cuál es el objetivo 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 está realizando su singleton es recurrente, y con diferentes parámetros cada vez, también puede 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 creación de instancias siempre será sin parámetros. De lo contrario, su singleton no es un singleton.


La razón por la que no puedes entender cómo lograr lo que intentas hacer es probablemente que lo que intentas hacer no tiene sentido. ¿Desea llamar a getInstance(x) con diferentes argumentos, pero siempre devuelve el mismo objeto? ¿Qué comportamiento desea cuando llama a getInstance(2) y luego a getInstance(5) ?

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

Si quiere que getInstance(2) y getInstance(5) devuelvan diferentes objetos, por otro lado, no está usando el patrón Singleton, está usando el patrón Factory.


Los singletons generalmente se consideran anti-patterns 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 usted escribió:

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 a getInstance dando como resultado la creación de más de una instancia (posiblemente con diferentes valores de x ).


Modificación del patrón de Singleton que usa la inicialización de Bill Pugh en el lenguaje 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); } }


Otra razón por la que los Singletons son un antipatrón es que, si se escriben de acuerdo con las recomendaciones, con el 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.


Puede agregar un método de inicialización para separar la creación de instancias de get.

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, llama a Singleton.init(123) una vez desde algún lugar para configurarlo, en el inicio de tu aplicación, por ejemplo.


Si desea crear una clase Singleton que sirva como Contexto, una buena forma es tener un archivo de configuración y leer los parámetros desde el archivo dentro de instance ().

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.


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

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


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

Si desea un conjunto fijo de objetos de valor inmutables, 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 implementación de Map . Por supuesto, cuando se trata de estática, tenga cuidado con el subprocesamiento (ya sea que se sincronice lo suficiente o utilice un ConcurrentMap ya sea verificando que otro subproceso no lo haya vencido o use alguna forma de futuro).


Sorprendido de que nadie mencionó cómo se crea / recupera un registrador. Por ejemplo, a continuación se muestra cómo se recupera Log4J logger .

// 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 method que prácticamente le dice todo sobre cómo funciona. Utiliza una tabla hash para almacenar los registradores que salen y la clave se deriva del nombre. Si el registrador no existe para un nombre dado, usa 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 } ...


También puede usar el patrón de Generador 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ías crearlo / instanciarlo / parametrizarlo de la siguiente manera:

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


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

Singleton.getInstance().setX(value);