Pregunta

Estoy usando Castle Windsor para un proyecto favorito en el que estoy trabajando.Estoy empezando a notar que necesito llamar al contenedor IoC en diferentes lugares de mi código para crear nuevos objetos.Esta dependencia del contenedor hace que mi código sea más difícil de mantener.

Hay dos soluciones que he usado para resolver este problema.

Intenté crear fábricas abstractas como envoltorios alrededor del contenedor que podría inyectar en partes de mi aplicación que necesitan crear objetos.Esto funciona pero tiene algunos inconvenientes porque a Castle le resulta difícil inyectar su propio contenedor como dependencia.Así que tengo que hacerlo a mano, esto anula el propósito del contenedor IoC.

He utilizado la clase principal de controlador de aplicaciones para empaquetar el contenedor de IoC y trabajar como una fábrica/repositorio central.Esto fue bastante exitoso, pero esta clase se está volviendo demasiado grande y actúa como un objeto dios central, casi todos los demás objetos tienen una referencia a él.

Ambas soluciones funcionan, pero ambas tienen sus inconvenientes.Por eso tengo curiosidad por saber si otras personas tuvieron el mismo problema y encontraron mejores soluciones.


editarEl problema no es para el objeto A que depende del objeto B.Aquí suelo utilizar la inyección de constructor y todo funciona.A veces tengo objetos de tipo A que necesitan crear un número variable de otros objetos de tipo B durante su vida.No estoy seguro de cómo hacer esto.

@Blair Conrad:Los problemas de mantenimiento no son graves hasta ahora.Algunas clases dependían del objeto contenedor que llamaba a container.Resolve<>.Y no quiero que mi código dependa de lo que creo que es la infraestructura.Todavía estoy probando cosas, así que noté que tenía que cambiar mucho código al cambiar de ninject a castle para este proyecto.

@flores:Mmm.Me gusta tu solución de puños.Combina las cosas que funcionan con ambas soluciones que he probado.Creo que todavía estaba pensando demasiado en los objetos y no lo suficiente en las interfaces/responsabilidades.Probé fábricas especialmente diseñadas, pero me gustaría que usaran el contenedor detrás de escena para crear los objetos y no he descubierto cómo puedo DI el contenedor en objetos de una manera limpia.

¿Fue útil?

Solución

El principal beneficio de la inyección de dependencia, al menos en mis aplicaciones, es la capacidad de escribir código independiente del contexto.Desde esa perspectiva, su segunda solución parece realmente subvertir el beneficio que DI podría brindarle.Si el 'objeto divino' expone diferentes interfaces para cada clase que hace referencia a él, puede que no sea demasiado malvado.Pero si llegaste tan lejos no veo por qué no lo llevas hasta el aro.

Ejemplo:Su objeto Dios tiene un método getFoo() y un método getBar().El objeto A necesita un Foo, el objeto B necesita una barra.Si A solo necesita un Foo, Foo debería inyectarse directamente en A y A no debería ser consciente de Dios en absoluto.Pero si A necesita seguir creando Foos, darle a A una referencia a Dios es prácticamente inevitable.Pero puedes protegerte del daño causado al pasar a Dios de un lado a otro limitando el tipo de referencia a Dios.Si haces que Dios implemente FooFactory y le das a A una referencia a FooFactory implementado por Dios, aún puedes escribir el código en A de una manera contextualmente neutral.Eso mejora las oportunidades de reutilización del código y aumenta su confianza en que un cambio hacia Dios no causará efectos secundarios inesperados.Por ejemplo, al eliminar getBar() de God, puede estar seguro de que la clase A no se romperá.

PERO ...Si de todos modos va a tener todas esas interfaces, probablemente sea mejor escribir clases de fábrica especialmente diseñadas y conectar todos sus objetos, incluidas las fábricas, dentro del contenedor, en lugar de envolver el contenedor en absoluto.El contenedor aún puede configurar las fábricas.

Otros consejos

¡Por favor, nunca utilices clases estáticas como IoC.Container.Resolve o ContainerFactory.GetContainer!

Esto hace que el código sea más complicado, más difícil de probar, mantener, reutilizar y leer.

Normalmente, cualquier componente o servicio tiene un solo punto de inyección: ese es el constructor (con propiedades opcionales).Y, en general, sus componentes o clases de servicios nunca deberían conocer la existencia de un contenedor.

Si sus componentes realmente necesitan tener una resolución dinámica interna (es decir,resolver la política de manejo de excepciones o el flujo de trabajo, según el nombre), entonces recomiendo considerar otorgar poderes al COI a través de proveedores altamente específicos

Si bien aprecio lo explícito de las "fábricas especialmente diseñadas" e incluso las uso yo mismo, esto parece un olor a código en mis propios diseños porque la interfaz pública (pequeña "i") sigue cambiando con una nueva fábrica y/o un nuevo método GetX. para cada implementación.Después de leer el libro de Jeremy Miller Es hora de una distensión de los contenedores de la COI, Sospecho que los genéricos e inyectar el contenedor en sí es el camino a seguir.

Envolvería Ninject, StructureMap o Windsor en algún tipo de interfaz IServiceLocator como la propuesta en el artículo de Jeremy.Luego, tenga una fábrica de contenedores que simplemente devuelva un IServiceLocator en cualquier parte de su código, incluso en bucles como sugirió originalmente.

IServiceLocator container = ContainerFactory.GetContainer(); 
while( keepLooping )
{
    IExample example = container.GetInstance<IExample>();
    keepLooping = example.DoWork();
}

Su fábrica de contenedores siempre puede devolver la misma instancia, puede intercambiar marcos de IoC, lo que sea.

Como seguimiento a @flipdoubt

Si termina utilizando un patrón de tipo localizador de servicios, es posible que desee consultar http://www.codeplex.com/CommonServiceLocator.Tiene algunos enlaces disponibles para varios marcos IoC populares (windsor, Structuremap) que podrían resultar útiles.

Buena suerte.

En este caso, recomendaría utilizar fábricas fuertemente tipadas, como mencionaste, que se inyectan.Esas fábricas pueden envolver el contenedor, pero pueden permitir pasarlo en un contexto adicional y realizar una manipulación adicional.Por ejemplo, Create en OrderFactory podría aceptar parámetros contextuales.

Tener dependencias estáticas en un localizador de servicios genérico es una mala idea ya que se pierde la intención y el contexto.Cuando un IoC crea una instancia, puede proporcionar las dependencias correctas en función de una serie de factores como el perfil, el contexto, etc., ya que tiene el panorama general.

CommonServiceLocator no es para este propósito, aunque uno podría verse tentado a utilizarlo.El objetivo principal de CommonServiceLocator es para aplicaciones/marcos que desean ser compatibles con contenedores de IoC.Sin embargo, las aplicaciones que lo usan solo deben llamar al localizador de manera óptima una vez para construir una jerarquía de componentes y sus dependencias.Nunca más se le debe llamar directamente.Si tuviéramos alguna forma de hacer cumplir eso, lo habríamos hecho.en prisma (http://www.microsoft.com/compositewpf) introdujimos un IContainerFacade para construir módulos.Se trata de un localizador de servicios, aunque de bajo nivel.En retrospectiva, probablemente deberíamos haber creado ModuleFactory o algo así y usar IContianerFacade para controlarlo, y luego usar eso para resolver módulos en lugar de ir directamente a Facade.En retrospectiva es 20/20.Sin embargo, es un nivel lo suficientemente bajo como para que realmente no afecte las cosas.

En CSL, luchamos con el nombre porque podría generar confusión.Al final nos decidimos por CSL porque técnicamente la interfaz no permitía hacer DI.

Ese es un problema realmente común.Windsor construido en Instalación de fábrica mecanografiada te brindará los beneficios de utilizar una fábrica, sin los inconvenientes mencionados.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top