2010-03-17 17 views
18

Aquí es mi muestra clase abstracta Singleton:¿Cómo puedo implementar una clase singleton abstracta en Java?

public abstract class A { 
    protected static A instance; 
    public static A getInstance() { 
     return instance; 
    } 
    //...rest of my abstract methods... 
} 

Y aquí es la aplicación concreta:

public class B extends A { 
    private B() { } 
    static { 
     instance = new B(); 
    } 
    //...implementations of my abstract methods... 
} 

Por desgracia, no puede obtener el código estático en la clase B para ejecutar, por lo que la variable de instancia nunca se establece. He intentado esto:

Class c = B.class; 
A.getInstance() - returns null; 

y esto

ClassLoader.getSystemClassLoader().loadClass("B"); 
A.getInstance() - return null; 

Correr tanto éstos en el depurador de Eclipse el código estático que nunca se ejecuta. La única forma que pude encontrar para ejecutar el código estático es cambiar la accesibilidad en el constructor de B a público y llamarlo.

Estoy usando sun-java6-jre en Ubuntu 32bit para ejecutar estas pruebas.

Respuesta

19

Resumen Singleton? No me parece viable. El patrón Singleton requiere un constructor private y esto ya imposibilita la creación de subclases. Deberás replantear tu diseño. El Abstract Factory pattern puede ser más adecuado para el propósito particular.

+0

El propósito del constructor privado en Singleton es evitar que alguien más lo instancia. Lo he logrado aquí: no se puede crear una instancia de una clase abstracta, y la subclase tiene un constructor privado. – Simon

+0

No obliga a que la subclase sea solo abstracta y solo tenga un constructor privado. – BalusC

+0

Singleton no _require_ un constructor privado (ver mi respuesta para una solución a esta pregunta con un constructor público). – ryvantage

4

A.getInstance() nunca llamará a una instancia derivada ya que está enlazada estáticamente.

Yo separaría la creación del objeto del propio objeto real y crearía un factory apropiado que devuelva un tipo de clase particular. No está claro cómo lo parametrizaría, dado su código de ejemplo: ¿está parametrizado a través de algún argumento, o la selección de clase es estática?

Es posible que desee volver a pensar el singleton, por cierto. Es un antipatrón común y hace que las pruebas (en particular) sean un problema, ya que las clases bajo prueba proporcionarán su propia instancia de esa clase como singleton. No puede proporcionar una implementación ficticia ni (fácilmente) crear una nueva instancia para cada prueba.

+0

A.getInstance() no necesita llamar a una instancia derivada, ya que devuelve la variable de instancia. La instancia derivada (clase B) establece la variable de instancia. Excepto que el bloque de código estático no se ejecuta. Si no puedo solucionar esto, entonces un patrón de fábrica puede ser mi única opción. – Simon

4

Singletons son el tipo de yucky. El resumen insiste en la herencia que, con mayor frecuencia, desea avoid si es posible. En general, me replantearía si lo que estás tratando de hacer es simplest possible way, y si es así, asegúrate de usar una fábrica y no un singleton (los singleton son muy difíciles de sustituir en unit tests, mientras que a las fábricas se les puede indicar que sustituyan fácilmente las instancias de prueba)

Una vez que comience a buscar implementarlo como una fábrica, lo abstracto se resolverá por sí solo (o será claramente necesario o se puede descartar fácilmente en lugar de una interfaz).

+0

+1 para señalar que los singletons son un antipatrón. –

+1

Los singleton tienen su lugar, pero generalmente hay un mejor patrón de diseño para lo que sea que esté haciendo. Un registro es probablemente el mejor ejemplo que se me ocurre para un singleton: de esta forma no tiene que pasar una variable y puede acceder a ella desde cualquier lugar. –

+0

Un registro se puede hacer mucho mejor con Inyección. Un registro singleton no puede decirle fácilmente de qué clase proviene la línea, mientras que Log l = new Log (this.getClass()) o alguna de sus variantes puede hacerlo. Con la inyección es solo @inject log o algo similar. Un singleton también es más difícil de probar, incluso para el registro. Una vez más, un singleton es casi la peor opción (pero se puede usar) –

2

Además de los problemas que otros han señalado, que tiene en el campo instanceA significa que sólo puede tener uno Singleton en toda la máquina virtual. Si también tiene:

public class C extends A { 
    private C() { } 
    static { 
     instance = new C(); 
    } 
    //...implementations of my abstract methods... 
} 

... entonces cualquiera de B o C se carga última va a ganar, y se perderán instancia singleton del otro.

Esta es solo una mala forma de hacer las cosas.

+0

Gracias, y los demás que dijeron lo mismo. Esto resalta que mi verdadero problema es cómo selecciono cuál de las varias implementaciones de mi interfaz debo exponer. Fábrica o Fachada parecen buenas maneras de ir. – Simon

8

Bien desde su publicación, incluso si no está claramente indicado, parece que desea que la clase abstracta juegue dos roles diferentes. Las funciones son:

  • el papel de la fábrica abstracta para un servicio (Singleton) que puede tener múltiples sustituibles implementaciones,
  • el papel de interfaz de servicio ,

además desea que el el servicio ser singleton impone 'singletoness' en toda la familia de clases, por alguna razón no es suficiente para que guarde en caché la instancia del servicio.

Esto está bien.

Alguien dirá que huele muy mal porque "viola la separación de las preocupaciones" y "los singleton y las pruebas unitarias no van bien juntas".

Alguien más dirá que está bien porque le da la responsabilidad de instanciar a los niños correctos en la familia y también expone una interfaz más fluida en general ya que no necesita mediación de una fábrica que no hace más que exponer un método estático

Lo que está mal es que después de que desee que los niños sean los responsables de seleccionar qué implementación debe devolver el método de fábrica principal. Está mal en términos de diseño porque está delegando a todos los niños lo que simplemente puede ser empujado hacia arriba y centralizado en la superclase abstracta y también muestra que están mezclando patrones que se usan en diferentes contextos, Abstract Factory (padres deciden qué familia de las clases que recibirán los clientes) y el Método de Fábrica (las fábricas secundarias seleccionan qué obtendrán los clientes).

Método de fábrica no solo no es necesario, sino que tampoco es posible con los métodos de fábrica, ya que se centra en la implementación o anulación de métodos de "instancia". No existe una anulación para métodos estáticos, ni para un constructor.

Volviendo a la idea inicial buena o mala de un singleton abstracto que selecciona qué comportamiento exponer hay varias maneras de resolver el problema inicial, Uno podría ser el siguiente, se ve mal, pero creo que está cerca de lo Estabas buscando:

public abstract class A{ 
    public static A getInstance(){ 
     if (...) 
     return B.getInstance(); 
     return C.getInstance(); 
    } 

    public abstract void doSomething(); 

    public abstract void doSomethingElse(); 

} 

public class B extends A{ 
    private static B instance=new B(); 

    private B(){ 
    } 

    public static B getInstance(){ 
     return instance; 
    } 

    public void doSomething(){ 
     ... 
    } 
    ... 
} 

//do similarly for class C 

El padre también podría utilizar la reflexión.

Solución más amigable y amigable con la extensión es simplemente tener hijos que no sean simples pero empacados en un paquete interno que documentará como padre "privado" y abstracto que puede exponer el gettonstate estático "singleton mimiking"() y guardará en memoria caché las instancias de los hijos que imponen que los clientes siempre obtengan la misma instancia de servicio.

Cuestiones relacionadas