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!
Respuestas:
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:
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:
En cualquier caso, la instanciación siempre será sin parámetros. De lo contrario, su singleton no es un singleton.
fuente
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
HashMap
oConcurrentHashMap
mapeado (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:
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.
fuente
Puede agregar un método de inicialización configurable para separar la instanciación de la obtención.
Luego puede llamar
Singleton.init(123)
una vez para configurarlo, por ejemplo, en el inicio de su aplicación.fuente
También puede usar el patrón Builder si desea mostrar que algunos parámetros son obligatorios.
Entonces podría crear / instanciar / parametrizarlo de la siguiente manera:
fuente
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.
fuente
Use getters y setters para establecer la variable y hacer que el constructor predeterminado sea privado. Luego use:
fuente
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 .
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.
fuente
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):
fuente
finally { RInterfaceHL.rloopHandler = null; }
engetInstance
, 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 utilizaRInterfaceHL
ctor para obtener algunos valores y no para mantener una referencia a él.return SingletonHolder.INSTANCE
funcionaría igual de biengetInstance
. 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 querloopHandler
necesita 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.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 llamasgetInstance(2)
y luegogetInstance(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)
ygetInstance(5)
devolver diferentes objetos, por otro lado, no está utilizando el patrón Singleton, está utilizando el patrón Factory.fuente
En su ejemplo, no está utilizando un singleton. Tenga en cuenta que si hace lo siguiente (suponiendo que Singleton.getInstance sea realmente estático):
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 un
enum
. 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.
fuente
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.
fuente
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.
fuente
Esto no es un singleton, pero puede ser algo que podría solucionar su problema.
fuente
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?
fuente
¿No podríamos hacer algo como esto?
fuente
A pesar de lo que algunos pueden afirmar, aquí hay un singleton con parámetros en el constructor
El patrón singleton dice:
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.
fuente
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í.
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.fuente
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).
fuente
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
Map
implementación. Por supuesto, cuando se trata de estadísticas, tenga cuidado con los subprocesos (sincronice lo suficiente o useConcurrentMap
comprobando que otro hilo no lo ha vencido o use alguna forma de futuros).fuente
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:
Su singleton tampoco es seguro para subprocesos, ya que varios subprocesos pueden realizar llamadas simultáneas para
getInstance
que se cree más de una instancia (posiblemente con diferentes valores dex
).fuente