¿Qué es una alternativa a Singleton?

114

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 arquitectónica, nos dijeron que elimináramos el singleton. Vimos algunos beneficios de no usar singleton en las pruebas unitarias porque podemos probar diferentes configuraciones a la vez.

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

John Mill
fuente

Respuestas:

131

El blog de pruebas de Google tiene una serie de entradas sobre cómo evitar Singleton (para crear código comprobable). Tal vez esto pueda ayudarle:

El último artículo explica en detalle cómo trasladar la creación de nuevos objetos a una fábrica, para evitar el uso de singleton. 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.

FrankS
fuente
3
*** Uso de inyección de dependencia para evitar singletons
Justin
Estos artículos son tan buenos como los estándares de programación de Google C ++.
2
Bueno en realidad no. El consejo de "No utilizar métodos estáticos" va directamente en contra del principio de interfaz mínima de Scott Meyers / Herb Sutters, por ejemplo. Hay consejos útiles, pero carecen de la contribución de múltiples mentes.
Matthieu M.
@FrankS ¿por qué cambiaste el orden de los enlaces? Al principio estaba en buen estado cronológico.
Cregox
@Cawas en realidad no tenía idea, eso fue hace más de cuatro años, así que supongo que tenía algunas razones para eso en ese entonces :-)
FrankS
15

La mejor forma es utilizar un patrón de fábrica. Cuando construye una nueva instancia de su clase (en la fábrica), puede insertar los datos 'globales' en el objeto recién construido, ya sea como una referencia a una sola instancia (que almacena 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.

gbjbaanb
fuente
1
No estoy de acuerdo con la declaración de "mejor manera", pero +1 para una buena alternativa.
tylermac
El problema con este enfoque es que cada objeto nuevo contiene (o hace referencia) lo que potencialmente puede ser una gran cantidad de datos. var_dump () en cualquiera de esos objetos que contienen gob resulta muy rápidamente en listados enormes salpicados libremente de advertencias de recursividad . Es jodidamente feo, no puede ser terriblemente eficiente y hace que las cosas parezcan locas. Sin embargo, personalmente no he encontrado una mejor manera. Doblé el método de "fábrica" ​​para usar el __construct () para hacer referencia a un global. Sin embargo, parece que todo se ha hecho todo lo posible para evitar el temido Singleton ...
FYA
2
@EastGhostCom: también podríamos utilizar el Singleton y dejar de tratar de hacer las cosas difíciles para nosotros mismos :)
gbjbaanb
5

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

De esa 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 suelo tomar!


fuente
4

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

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

Gene Gotimer
fuente
3
Oh, la ironía: use (singleton) Spring beans para reemplazar su singleton ...
Zack Macomber
4

La alternativa es pasar lo que necesita en lugar de pedirle cosas a un objeto.

Koen
fuente
4

no acumule responsabilidades en un solo objeto de configuración, ya que terminará en un objeto muy grande, difícil de entender y frágil.

Por ejemplo, si necesita otro parámetro para una clase en particular, cambia el Configurationobjeto y luego recompila todas las clases que lo usan. Esto es algo problemático.

Intente refactorizar su código para evitar un objeto común, global y grande Configuration. Pase solo los parámetros requeridos a las clases de cliente:

class Server {

    int port;

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

}

debe refactorizarse para:

 class Server {

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

un marco de inyección de dependencia ayudará mucho aquí, pero no es estrictamente necesario.

dfa
fuente
Sí, es un buen punto. He hecho esto antes. Mi gran objeto de configuración estaba implementando interfaces como MailServiceConf, ServerConf ... que el marco de inyección de dependencia pasa la configuración a las clases, por lo que mis clases no dependían del gran objeto de configuración.
caltuntas
1

Puede lograr el mismo comportamiento de singleton utilizando métodos estáticos. Steve yegge lo explica muy bien en este post.

Youssef
fuente
En realidad, el artículo es bastante bueno y no dice que debas usar métodos estáticos en su lugar. En su lugar, afirma que los métodos estáticos también son simples singleton y al final recomienda usar el patrón del método de fábrica: "Terminaré diciendo que si todavía siente la necesidad de usar objetos Singleton, considere usar el patrón de método de fábrica en su lugar. ... "
FrankS
0

¿Es posible una clase que contenga solo métodos y campos estáticos? No estoy seguro de cuál es exactamente su situación, pero valdría la pena investigarla.

Thomas Owens
fuente
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.
0

Depende de qué herramientas / marcos, etc. se estén utilizando. Con las herramientas de inyección de dependencia / ioc, a menudo se puede obtener rendimiento / optimizaciones de singleton haciendo que el contenedor di / ioc use el comportamiento de singleton para la clase requerida (como una interfaz IConfigSettings) creando solo una instancia de la clase. Esto aún podría sustituirse por pruebas

Alternativamente, uno podría usar una fábrica para crear la clase y devolver la misma instancia cada vez que la solicite, pero para probar, podría devolver una versión stubned / simulada

saret
fuente
0

Revise la posibilidad de realizar la configuración como interfaz de devolución de llamada. Entonces su código sensible a la configuración se verá:

MyReuseCode.Configure(IConfiguration)

El código de inicio del sistema se verá:

Library.init(MyIConfigurationImpl)
Dewfy
fuente
0

Podría usar un marco de inyección de dependencia para aliviar el dolor de pasar el objeto de configuración. Uno decente es ninject, que tiene la ventaja de usar código en lugar de xml.

Colin Gravill
fuente
0

Tal vez tampoco sea muy limpio, pero tal vez podría pasar los bits de información que desea cambiar al método que crea el singleton, en lugar de usar

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

puede llamar createSingleton(Information info)directamente al inicio de la aplicación (y en los métodos de configuración de las pruebas unitarias).

Roland Ewald
fuente
-1

Los singleton no son malvados, pero el patrón de diseño es defectuoso. Tengo una clase de la que solo quiero crear una instancia única durante el tiempo de ejecución, pero quiero crear varias instancias aisladas durante las pruebas unitarias para garantizar resultados deterministas.

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

codematrix
fuente