Los singleton son como el comunismo: ambos suenan muy bien en el papel, pero explotan con problemas en la práctica.
El patrón singleton pone un énfasis desproporcionado en la facilidad de acceso a los objetos. Evita por completo el contexto al requerir que cada consumidor use un objeto de ámbito de dominio de aplicación, sin dejar opciones para diferentes implementaciones. Incorpora el conocimiento de la infraestructura en sus clases (la llamada a GetInstance()
) mientras agrega exactamente cero poder expresivo. En realidad, disminuye su poder expresivo, porque no puede cambiar la implementación utilizada por una clase sin cambiarla para todas . Simplemente no puede agregar piezas únicas de funcionalidad.
Además, cuando la clase Foo
depende de Logger.GetInstance()
, Foo
está ocultando efectivamente sus dependencias a los consumidores. Esto significa que no puede comprenderlo completamente Foo
o usarlo con confianza a menos que lea su fuente y descubra el hecho de que depende Logger
. Si no tiene la fuente, eso limita qué tan bien puede comprender y usar de manera efectiva el código del que depende.
El patrón singleton, implementado con propiedades / métodos estáticos, es poco más que un truco para implementar una infraestructura. Le limita de innumerables formas sin ofrecer ningún beneficio discernible sobre las alternativas. Puede usarlo como desee, pero como existen alternativas viables que promueven un mejor diseño, nunca debería ser una práctica recomendada.
Otros han explicado muy bien el problema de los singleton en general. Solo me gustaría agregar una nota sobre el caso específico de Logger. Estoy de acuerdo con usted en que generalmente no es un problema acceder a un registrador (o al registrador raíz, para ser precisos) como singleton, a través de un método estático
getInstance()
ogetRootLogger()
. (a menos que desee ver lo que registra la clase que está probando, pero en mi experiencia, apenas puedo recordar casos en los que esto fue necesario. Por otra parte, para otros, esto podría ser una preocupación más urgente).En mi opinión, un registrador singleton no es una preocupación, ya que no contiene ningún estado relevante para la clase que está probando. Es decir, el estado del registrador (y sus posibles cambios) no tienen ningún efecto sobre el estado de la clase probada. Por lo tanto, no dificulta las pruebas unitarias.
La alternativa sería inyectar el registrador a través del constructor en (casi) todas las clases de su aplicación. Para la coherencia de las interfaces, debe inyectarse incluso si la clase en cuestión no registra nada en este momento; la alternativa sería que cuando descubra en algún momento que ahora necesita registrar algo de esta clase, necesita un registrador, por lo tanto necesita agregar un parámetro de constructor para DI, rompiendo todo el código del cliente. No me gustan estas dos opciones, y siento que usar DI para el registro sería simplemente complicar mi vida para cumplir con una regla teórica, sin ningún beneficio concreto.
Entonces, mi conclusión es: una clase que se usa (casi) universalmente, pero que no contiene un estado relevante para su aplicación, se puede implementar de manera segura como Singleton .
fuente
Se trata principalmente, pero no completamente, de pruebas. Los singlton eran populares porque era fácil consumirlos, pero los singleton tienen una serie de desventajas.
DI le brinda el fácil consumo de sus clases dependientes, simplemente colóquelo en los argumentos del constructor y el sistema se lo proporcionará, al mismo tiempo que le brinda la flexibilidad de prueba y construcción.
fuente
Casi la única vez que debería usar un Singleton en lugar de Dependency Injection es si el Singleton representa un valor inmutable, como List.Empty o similar (asumiendo listas inmutables).
La prueba instintiva para un Singleton debería ser "¿estaría bien si esto fuera una variable global en lugar de un Singleton?" De lo contrario, está utilizando el patrón Singleton para empapelar una variable global y debería considerar un enfoque diferente.
fuente
Acabo de consultar el artículo de Monostate: es una excelente alternativa a Singleton, pero tiene algunas propiedades extrañas:
¿No es esto un poco aterrador, porque el asignador realmente depende de la conexión de la base de datos para realizar el guardado (), pero si se ha creado otro asignador antes, puede omitir este paso para adquirir sus dependencias? Aunque ordenado, también es un poco desordenado, ¿no?
fuente
Hay otras alternativas a los patrones Singleton: Proxy y MonoState.
http://www.objectmentor.com/resources/articles/SingletonAndMonostate.pdf
¿Cómo se puede utilizar el patrón de proxy para reemplazar un singleton?
fuente