2010-08-13 16 views
9

Hay muchas preguntas sobre si los singleton son "malos" y qué patrones usar en su lugar. En general, se centran en el patrón de diseño singleton, que implica recuperar la instancia singleton de un método estático en la clase. Esta no es una de esas preguntas.Estoy usando Dependency Injection: ¿qué tipos debo enlazar como singletons?

Desde que realmente "descubrí" la inyección de dependencia hace varios meses, he impulsado su adopción en nuestro equipo, eliminando patrones estáticos y únicos de nuestro código a lo largo del tiempo, y usando inyección basada en constructor siempre que sea posible. Hemos adoptado convenciones para que no tengamos que seguir agregando enlaces explícitos a nuestros módulos DI. Incluso utilizamos el marco DI para proporcionar instancias de registrador, de modo que podamos decirle automáticamente a cada registrador en qué clase está sin código adicional. Ahora que tengo un solo lugar donde puedo controlar cómo se enlazan los distintos tipos, es muy fácil determinar cuál debería ser el ciclo de vida de una categoría particular de clases (clases de utilidad, repositorios, etc.).

Mi pensamiento inicial fue que probablemente habría algunas ventajas para las clases vinculantes como singleton si esperaba que se utilizaran con bastante frecuencia. Simplemente significa que hay mucho menos new en curso, especialmente cuando el objeto que está creando termina teniendo un gran árbol de dependencias. Casi todos los campos no estáticos en cualquiera de estas clases son esos valores que se inyectan en el constructor, por lo que hay relativamente poca carga de memoria para mantener una instancia cuando no se está utilizando.

Luego leí "Singleton, te amo, pero me estás derribando" en www.codingwithoutcomments.com, y me hizo preguntarme si tenía la idea correcta. Después de todo, Ninject compila las funciones de creación de objetos por defecto, por lo que hay muy poca sobrecarga de reflexión involucrada en la creación de instancias adicionales de estos objetos. Debido a que estamos manteniendo la lógica comercial fuera del constructor, crear nuevas instancias es una operación realmente liviana. Y los objetos no tienen un montón de campos no estáticos, por lo que tampoco hay mucha sobrecarga de memoria involucrada en la creación de nuevas instancias.

Por lo tanto, en este punto, estoy empezando a pensar que probablemente no importe mucho de ninguna manera. ¿Hay consideraciones adicionales que no he tenido en cuenta? ¿Alguien realmente ha experimentado una mejora significativa en el rendimiento al cambiar los ciclos de vida de ciertos tipos de objetos? ¿Seguir el patrón DI es la única cosa realmente importante, o hay otras razones por las que el uso de una sola instancia de un objeto puede ser intrínsecamente "malo"?

Respuesta

5

Estoy usando instancias singleton para caché para crear objetos caros (como la fábrica de sesiones nhibernate) o para objetos que deseo compartir en la aplicación.

En caso de incertidumbre, preferiría que el objeto se vuelva a crear cada vez que se use y marcarlo como singleton o "por hilo"/"por solicitud"/"por lo que sea" solo si realmente lo necesita.

La dispersión de ubicaciones únicas para potencialmente guardar algunas actualizaciones es una optimización prematura y puede llevar a errores realmente desagradables porque no puede estar seguro de si el objeto es nuevo o está compartido en varios objetos/instancias.

Incluso si el objeto no tiene estado en este momento y parece guardar para compartir, alguien puede refactorizar más tarde y agregarle estado, que luego puede ser compartido involuntariamente entre diferentes objetos/instancias.

+0

+1 para responder la pregunta que se hizo. – StriplingWarrior

0

No debería importar mucho el uso de la memoria, pero obtendrá una mayor modularidad, una capacidad de prueba más sencilla con objetos simulados, y la fealdad estética de tener que escribir dependencias explícitamente alentará a los programadores a hacer que cada clase sea más ortogonal e independiente . Es más fácil ignorar las dependencias cuando tu clase llama a globales, lo que básicamente es un singleton, y puede hacerte la vida más difícil más adelante.

+0

No creo que haya entendido mi pregunta. Yo * estoy * usando la inyección de dependencia, por lo que el código se verá exactamente igual. La pregunta es si los objetos inyectados deberían ser siempre la misma instancia cada vez, o si hay alguna ventaja de tener una nueva instancia entregada a cada clase. – StriplingWarrior

+1

Oh, en ese caso, debe haber más información para responder su pregunta correctamente. Si los objetos pasados ​​no tienen estado, entonces no hay diferencia, excepto la referencia de objeto adicional requerida para crear una nueva instancia. Si lo hacen, entonces depende de sus requisitos y de lo que espera que suceda. – gtrak

+0

Los objetos no tienen ningún estado aparte de otros servicios inyectados, que se almacenan como campos privados. Entonces, potencialmente hay un gran árbol de 'nuevo's sucediendo cada vez que se crea un servicio, pero eso es todo. – StriplingWarrior

1

No estoy seguro de si la gestión de memoria es el gran problema a menos que esté creando MUCHOS objetos. Descubrí que el uso de singleton desde mi contenedor IoC es útil cuando se trata de almacenamiento en caché, donde la creación del objeto de almacenamiento en caché y la conexión a los servidores de almacenamiento en caché es costoso durante la creación, así que creamos el objeto de caché una vez y lo reutilizamos.

Una sola instancia puede ser perjudicial, sin embargo, puede crear un cuello de botella en el rendimiento, ya que tiene una única instancia que sirve múltiples solicitudes en el caso de los servicios.

+0

¿Puede profundizar en ese segundo párrafo? Asumiendo que mis clases están escritas para que cada método esté encapsulado, entonces la seguridad de la secuencia no es un problema (no tengo que hacer bloqueos ni nada), ¿cómo puede ralentizar las cosas para que una instancia sirva múltiples solicitudes? – StriplingWarrior

+1

Si está seguro de subprocesos, entonces no hay nada de lo que preocuparse, hasta donde yo sé. Mi primer intento con IoC hace un tiempo me golpeó con este problema. Desde entonces utilizo Singleton para muchos servicios y los vuelvo seguros. – Wix

0

, como se indica en Gary's answer, el objetivo de DI es la capacidad de prueba (puede simular fácilmente todas las referencias a una clase ya que todas figuran en el constructor), no el rendimiento en tiempo de ejecución.

Usted quiere hacer TDD (desarrollo de prueba), ¿verdad?

+0

Como noté en mi comentario sobre la respuesta de Gary (escrita antes de su respuesta), y en el título, y de nuevo el cuerpo de la pregunta: * Estoy usando inyección de dependencia *. No estoy preguntando sobre el "patrón" singleton, sino más bien si hay alguna ventaja para vincular mis servicios como singleton versus transitorio en el módulo de configuración DI. – StriplingWarrior

+0

Ya veo. Sigo pensando que el rendimiento no debería ser la principal preocupación aquí. Recomendaría utilizar la solución que hace que su código sea más fácil de leer. Usted dice que usa su marco DI para registradores; eso parece dar una ganancia de legibilidad (menos desorden en las clases). Para otras instancias, lo contrario podría ser cierto; es posible que desee ser explícito si el servicio alude a la intención de clase/ciclo de vida/categoría. – louisgab

+0

Debido a que estoy usando DI y vinculando por convención, puedo agregar '.InSingletonScope()' a una línea de código, y todas mis clases de repositorio mágicamente serán singletons, aunque sigo usando la inyección de dependencia para proporcionarlos a clases que los necesitan Gracias al marco DI, la legibilidad del código no se verá afectada de ninguna manera. Estoy de acuerdo en que el rendimiento no es una gran preocupación, por lo que mi pregunta es si hay otras preocupaciones que debo tener en cuenta al tomar esta decisión. – StriplingWarrior

Cuestiones relacionadas