2009-08-19 17 views
108

Tenemos una clase que contiene información de configuración para la aplicación. Solía ​​ser un singleton. Después de una revisión de arquitectura, nos dijeron que elimináramos el singleton. Vimos algunos beneficios de no usar Singleton en las pruebas unitarias porque podemos probar diferentes configuraciones todas a la vez.Lo que es alternativo a Singleton

Sin singleton, tenemos que pasar la instancia por todas partes en nuestro código. Se está poniendo muy sucio así que escribimos un contenedor singleton. Ahora estamos portando el mismo código para PHP y .NET, me pregunto si hay un patrón mejor que podamos usar para el objeto de configuración.

Respuesta

124

El Google Testing blog tiene una serie de entradas sobre cómo evitar Singleton (para crear un código comprobable). Tal vez esto le puede ayudar:

El último artículo explica en detalle cómo mover la creación de nuevos objetos en una fábrica, por lo puede evitar el uso de singletons.Vale la pena leerlo con seguridad.

En resumen, trasladamos a todos los nuevos operadores a una fábrica. Agrupamos todos los objetos de vida similar en una sola fábrica.

+11

Se requiere leer el blog de Google testing. – koen

+3

*** Uso de inyección dependiente para evitar singletons – Justin

+0

¡Estos artículos son tan buenos como los Estándares de programación de Google C++! –

0

¿Es una clase que contiene solo métodos estáticos y campos posibles? No estoy seguro de cuál es tu situación, pero valdría la pena investigarlo.

+1

Si la clase no tiene estado, debería ser una clase estática. – AlbertoPL

+1

Está en C++, el patrón se conoce como Monostate. –

15

La mejor manera es utilizar un patrón de fábrica en su lugar. Cuando construyes una nueva instancia de tu clase (en la fábrica) puedes insertar los datos 'globales' en el objeto recién construido, ya sea como referencia a una sola instancia (que almacenas en la clase de fábrica) o copiando los datos relevantes. datos en el nuevo objeto.

Todos sus objetos contendrán los datos que solían vivir en el singleton. No creo que haya mucha diferencia en general, pero puede hacer que su código sea más fácil de leer.

+1

No estoy de acuerdo con la declaración de "la mejor manera", pero +1 es una buena alternativa. – tylermac

+0

El problema con este enfoque es que cada nuevo objeto contiene (o referencias) lo que potencialmente puede ser una gran cantidad de datos. var_dump() en cualquiera de esos objetos que contienen gob se obtiene muy rápidamente en listas enormes salpicadas de ** advertencias de recursividad **. Es feo, no puede ser terriblemente eficiente, y hace que las cosas parezcan descontroladas. Sin embargo, personalmente no he encontrado una mejor manera. Invertí el método de "fábrica" ​​en el uso de __construct() para hacer referencia a un global. Sin embargo, se siente como si todo estuviera doblado hacia atrás para evitar el temido Singleton ... – FYA

+2

@EastGhostCom: también podríamos usar el singleton y dejar de tratar de complicarnos las cosas :) – gbjbaanb

5

Podría estar diciendo lo obvio aquí, pero ¿hay alguna razón por la cual no se puede usar un marco de inyección de dependencia como Spring o Guice? (Creo que Spring también está disponible para .NET también ahora).

De esta manera, el marco puede contener una sola copia de los objetos de configuración, y sus beans (servicios, DAO, lo que sea) no tienen que preocuparse por buscarlos.

¡Este es el enfoque que generalmente uso!

0

Tal vez no sea muy limpia, pero tal vez podría pasar los bits de información que desea han cambiado con el método que crea el singleton - en lugar de utilizar

public static Singleton getInstance() { 
    if(singleton != null) 
     createSingleton(); 
     return singleton; 
    } 
} 

que se podría llamar createSingleton(Information info) directamente al iniciar la aplicación (y en los métodos de configuración de las pruebas unitarias).

4

Si usa Spring Framework, puede simplemente crear un bean regular. De forma predeterminada (o si establece explícitamente scope="singleton") solo se crea una instancia del bean y esa instancia se devuelve cada vez que se usa el bean en una dependencia o se recupera a través del getBean().

Tiene la ventaja de la instancia única, sin el acoplamiento del patrón Singleton.

+1

Oh, la ironía - use (singleton) Spring frijoles para reemplazar su singleton ... –

0

Depende de qué herramientas/marcos, etc. se están utilizando. Con las herramientas de inyección/ioc a menudo todavía se puede obtener rendimiento/optimizaciones singleton haciendo que el contenedor di/ioc use el comportamiento singleton para la clase requerida (como una interfaz IConfigSettings) creando una única instancia de la clase. Esto podría ser todavía sale del juego para probar

Alternativamente se podría usar una fábrica para crear la clase y devolver la misma instancia cada vez que solicita - pero para probar que podía volver a/versión burlado apagó

4

La alternativa está pasando lo que necesita en lugar de pedirle cosas a un objeto.

0

Revisar la posibilidad de realizar la configuración como interfaz de devolución de llamada. código para la configuración de sensibilidad se verá:

MyReuseCode.Configure(IConfiguration) 

código del Sistema-init se verá:

Library.init(MyIConfigurationImpl) 
4

no se acumulan responsabilidades a un solo objeto de configuración ya que termina en una muy grande objeto que es difícil de entender y frágil.

Por ejemplo, si necesita otro parámetro para una clase particular, cambie el objeto Configuration, luego vuelva a compilar todas las clases que lo utilizan. Esto es algo problemático.

Intente refacturar su código para evitar un objeto común, global y grande Configuration. Pasar parámetros sólo se requieren para clases de cliente:

class Server { 

    int port; 

    Server(Configuration config) { 
     this.port = config.getServerPort(); 
    } 

} 

debe refactorizado a:

class Server { 

    public Server(int port) { 
     this.port = port; 
    } 
} 

un marco de inyección dependencia va a ayudar mucho aquí, pero no se requiere stricly.

+0

Sí, es un buen punto. He hecho esto antes. Mi gran objeto de configuración era implementar interfaces como MailServiceConf, ServerConf .. que el marco de Dependency Injection pasa la configuración a las clases para que mis clases no dependieran del gran objeto de Configuración. – mcaaltuntas

0

Puede usar un marco de inyección de dependencia para aliviar el dolor de pasar en el objeto de configuración. Una decente es ninject que tiene la ventaja de usar código en vez de xml.

0

Puede realizar el mismo comportamiento de singleton utilizando métodos estáticos. Steve Yegge lo explica muy bien en this publicación.

+0

En realidad, el artículo es bastante bueno, y no dice que deba usar métodos estáticos en su lugar. En su lugar, declara que los métodos estáticos también son simples y al final recomienda utilizar el patrón de método de fábrica: "Cerraré diciendo que si todavía siente la necesidad de usar objetos Singleton, considere usar el patrón Factory Method en cambio ... " – FrankS

-1

Los Singletons no son malos pero el patrón de diseño es defectuoso. Tengo una clase que solo quiero crear una instancia única de ella durante el tiempo de ejecución, pero quiero crear varias instancias aisladas durante la prueba unitaria para garantizar resultados deterministas.

DI utilizando Spring, etc., es una opción muy buena pero no la única.

+1

¿Cuáles son las otras opciones? – hakre

Cuestiones relacionadas