2011-01-16 23 views
8

He encontrado tres maneras de crear instancias de Singleton, pero tengo dudas sobre si alguna de ellas es la mejor. Los estoy usando en un entorno de subprocesos múltiples y prefiero la instanciación lenta.
Muestra 1:java singleton instanciation

private static final ClassName INSTANCE = new ClassName(); 

public static ClassName getInstance() { 
    return INSTANCE; 
} 

Muestra 2:

private static class SingletonHolder { 
    public static final ClassName INSTANCE = new ClassName(); 
} 

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

Muestra 3:

private static ClassName INSTANCE; 

public static synchronized ClassName getInstance() 
{ 
    if (INSTANCE == null) 
     INSTANCE = new ClassName(); 

    return INSTANCE; 
} 

El proyecto que estoy usando ATM utiliza Muestra 2 en todas partes, pero me gusta la Muestra 3 más. También está la versión Enum, pero simplemente no la entiendo.

La pregunta aquí es - ¿en qué casos debo/no debo usar ninguna de estas variaciones? Aunque no estoy buscando explicaciones largas (hay muchos otros temas al respecto, pero todos finalmente se convierten en discusiones sobre IMO), me gustaría que fuera comprensible con pocas palabras.

Respuesta

18

La manera más segura y fácil de implementar un producto único en Java es mediante el uso de enumeraciones (como usted ha mencionado):

public enum ClassName { 
    INSTANCE; 

    // fields, setters and getters 
} 

La enumeración semántica garantiza que no habrá una sola INSTANCE

Si no

utilizando el enfoque enum, debe ocuparse de muchos aspectos, como las condiciones de carrera y la reflexión. He estado rompiendo singletons de algunos frameworks, y abusando de ellos, porque no estaban escritos correctamente. La enum garantiza que nadie la romperá.

+0

¿Pero cómo lo uso? Por ejemplo, tengo esto: clase pública MyClass { public enum ClassName { INSTANCE; // pongo todos mis métodos aquí en lugar de ... } // aquí como lo haría normalmente? } Editar: maldita sea, sin formato en los comentarios ?? ¡Lo odio! – jurchiks

+1

usted escribe sus campos únicos y métodos a continuación. Y luego acceda a ellos a través de 'ClassName.INSTANCE.doSomething (foo)' – Bozho

+0

@jurchiks: sí. En otro lugar, debe llamar a 'ClassName.INSTANCE.methodName()' para usar los métodos singleton (o tal vez usar importaciones estáticas para deshacerse de la parte 'ClassName.INSTANCE'). – Carl

1

Ejemplo 1: Utilícelo si no necesita un Singleton perezosa.
Muestra 2: Nunca utilizar - se confunde con demasiadas clases. Y una clase interna solo para mantener una variable parece ser un poco innecesaria. Muestra 3: Este es un Singleton perezoso. Úselo si lo necesita.

2

Antes que nada, asegúrese de que necesita un singleton y de que desea proporcionar acceso de "nivel global" al singleton. Descubrí que en muchos casos los clientes del singleton no necesitan saber que es un singleton. Por el contrario, solo necesitan obtener un servicio cuando se crean instancias.

Por lo tanto, independientemente de cómo obtenga el singleton (si es que lo tiene), considere cambiar la forma en que sus clases obtienen acceso a este objeto. Si bien esto significa modificar los constructores y cambiar los "cambios de distribución", descubrí que los marcos de inyección de dependencias reducen el costo. El marco DI también puede encargarse de la instanciación de singleton (por ejemplo, Guice lo hace).

Aparte de eso. La opción 3 es la versión típica y más común (y segura para hilos) con la que estoy familiarizado. La opción 1 es principalmente para la inicialización no lenta (no siempre aceptable). Nunca he visto 2 usados.

+0

Bueno, la clase que quiero instanciar contiene 2 objetos Map con posiblemente muchos datos, dos métodos para cada uno Map para obtener valor usando key (y rellenar el valor si es nulo) y dos métodos que acceden a la base de datos (primero para poblar el valor de una clave seleccionada, segundo para insertar/actualizar/eliminar datos de la base de datos usando datos existentes). Sin contar getters/setters aquí. ¿Crees que no debería ser un singleton? – jurchiks

+0

No estoy seguro de si está entendiendo el núcleo de lo que estoy sugiriendo. Digamos que encapsulaste todo lo que acabas de describir como una interfaz y clase correspondiente llamada DBLookupService y DBLookupServiceImpl. Para las clases que usan LookupService, no importa de dónde viene, solo importa que obtengan una instancia de LookupService. Esto también hace que sea mucho más fácil de probar. – Uri

+0

En cuanto a si debe ser singleton, probablemente, pero podría pensar en un código optimizado multi-core en el que usa varias instancias (por ejemplo, si puede dividir los datos de manera significativa). Pero mi punto es que deberías hacer todo lo posible para evitar las llamadas a esa getInstance() desde el código que usa el servicio. Esto hará que las pruebas y los cambios futuros sean mucho más fáciles. – Uri

4

La muestra 1 no utiliza la inicialización diferida.

Las muestras 2 y 3 son flojas. La muestra 2 usa Initialization on demand holder idiom (IODH) que tiene sin sobrecarga de sincronización. Por lo tanto, es más rápido que la Muestra 3.

En Java efectivo (elemento 3), Joshua Bloch recomienda que un tipo de elemento enum es la mejor manera de implementar un singleton.

Sin embargo, si no está seguro acerca del tipo de enumeración, quédese con IODH.

+0

¿Por qué Sample 1 no usa la inicialización lenta? Creo que se está utilizando, ya que solo hay una instancia y se realiza una inicialización. – Sam003