24

Estoy usando castle windsor para un proyecto de mascota en el que estoy trabajando. Estoy comenzando a darme cuenta de que necesito llamar al contenedor IoC en diferentes lugares de mi código para crear nuevos objetos. Esta dependencia en el contenedor hace que mi código sea más difícil de mantener.IoC, ¿dónde colocas el contenedor?

Hay dos soluciones que he usado para resolver este problema

Intenté crear fábricas abstractos como envolturas alrededor del recipiente que podía inyectar en partes de mi aplicación que necesitan para crear objetos. Esto funciona pero tiene algunos inconvenientes porque Castle tiene dificultades para inyectar su propio contenedor como dependencia. Así que tengo que hacer eso a mano, este tipo de derrotas todo el propósito del contenedor IoC.

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

Ambas soluciones tipo de trabajo, pero ambas tienen sus inconvenientes. Así que tengo curiosidad si otras personas tuvieran el mismo problema y hubieran encontrado mejores soluciones.


edición El problema no es para un objeto que depende de objeto B. Aquí por lo general sólo tiene que utilizar la inyección de constructor y todo funciona. A veces tengo objetos de tipo A que necesitan crear una cantidad variable de otros objetos de tipo B durante su vida útil. No estoy seguro de cómo hacer esto.

@Blair Conrad: los problemas de mantenimiento no son graves hasta ahora. Tuve algunas clases que dependen del objeto contenedor que llama al contenedor. Resuelva <>. Y no quiero tener mi código dependiendo de lo que creo que es infraestructura. Todavía estoy probando cosas, así que noté que tenía que cambiar un montón de código cuando cambiaba de ninject a castle para este proyecto.

@flowers: Hmm. 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 objetos y no lo suficiente en interfaces/responsabilidades. Intenté construir fábricas con propósito, pero me gustaría que usen 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.

+0

Tengo curiosidad y las respuestas pueden ayudarnos a responder. ¿Qué tipo de problemas de mantenimiento ha tenido? –

Respuesta

3

El principal beneficio de Dependency Injection, al menos en mis aplicaciones, es la capacidad de escribir código que es independiente del contexto. Desde esa perspectiva, su segunda solución parece que realmente subvierte el beneficio que DI podría brindarle. Si el 'objeto divino' expone diferentes interfaces a cada clase que lo hace referencia, puede que no sea demasiado malo. Pero si llegaste tan lejos no veo por qué no te llevas todo el camino 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 estar consciente de Dios en absoluto. Pero si A necesita seguir creando Foos, darle a A una referencia a Dios es casi inevitable. Pero puedes protegerte del daño hecho pasando a Dios al reducir el tipo de la referencia a Dios. Si haces que Dios implemente FooFactory y le proporciones a A una referencia a FooFactory implementado por Dios, aún puedes escribir el código en A de una manera neutral al contexto. Eso mejora las oportunidades de reutilización del código y aumenta su confianza en que un cambio en Dios no causará efectos secundarios inesperados. Por ejemplo, puede estar seguro al eliminar getBar() de Dios que la clase A no se romperá.

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

2

Si bien aprecio el carácter explícito de las "fábricas con propósito" e incluso las uso yo mismo, esto se siente como un olor a código en mis propios diseños porque la interfaz pública (pequeña "i") cambia con una nueva fábrica y/o un nuevo método GetX para cada implementación. Después de leer el It's time for IoC Container Detente de Jeremy Miller, sospecho que los genéricos e inyectar el contenedor en sí es el camino a seguir.

Me gustaría envolver 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 lo 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 integridad, puede intercambiar marcos IoC, lo que sea.

11

Por favor, nunca use 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 para mantener, reutilizar y leer.

Normalmente, cualquier componente individual o servicio solo tiene un único punto de inyección: el constructor (con propiedades opcionales). Y, en general, sus componentes o clases de servicio no deberían saber nunca sobre la existencia de contenedores.

Si sus componentes realmente necesita tener resolución dinámica en el interior (la resolución es decir, la política de gestión de excepciones o de flujo de trabajo, basado en el nombre), entonces me recomiendan tener en cuenta lending IoC powers via the highly-specific providers

+0

Gracias por el enlace. Tengo que leerlo con más cuidado cuando tengo tiempo, pero creo que terminé probando algo así, luego decidí no usarlo y usé una fábrica abstracta para crear los objetos dinámicos en lugar de usar el contenedor IoC. Solo usé el contenedor para crear la fábrica. – Mendelt

+2

@Rinat, usando el escenario original de OP: estás en A, y necesitas n cantidad de instancias B. Sin referencia al contenedor, ¿cómo obtienes tus Bs? – flipdoubt

+0

Su enlace ahora no funciona ... ¿podría actualizarlo por favor? –

1

Como seguimiento a @flipdoubt

Si Si terminas usando un patrón de tipo de localizador de servicio, es posible que desees consultar http://www.codeplex.com/CommonServiceLocator. Tiene algunos enlaces disponibles para varios frameworks populares de IoC (windsor, structuremap) que pueden ser útiles.

Buena suerte.

+0

¡Gracias! Lo comprobaré. Descubrí que es muy fácil crear tu propio localizador de servicios, pero podría obtener ideas de cómo hicieron las cosas. – Mendelt

+0

Sin preocupaciones, buena suerte. – smaclell

1

Recomendaría en este caso utilizar fábricas fuertemente tipadas como usted mencionó que se inyectan. Esas fábricas pueden envolver el contenedor, pero pueden permitir pasar en contexto adicional y realizar un manejo adicional. Por ejemplo, Create on the OrderFactory podría aceptar parámetros contextuales.

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

CommonServiceLocator no es para este propósito, aunque uno podría estar tentado de usarlo. El objetivo principal de CommonServiceLocator es para aplicaciones/frameworks que desean ser compatibles con cross IoC container. Sin embargo, las aplicaciones que usan solo deben llamar al localizador de manera óptima una vez para crear una jerarquía de componentes y sus dependencias. Nunca debería llamarse directamente de nuevo. Si tuviéramos alguna manera de hacer cumplir eso tendríamos. En Prism (http://www.microsoft.com/compositewpf) presentamos un IContainerFacade para construir módulos. Es un localizador de servicios aunque de bajo nivel. En retrospectiva, probablemente deberíamos haber creado un ModuleFactory o algo así y haber usado IContianerFacade para obtenerlo, y luego usar esos módulos de resolución frente a ir directamente a Facade. Retrospectiva es 20/20.Aunque es de bajo nivel, realmente no afecta las cosas.

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

0

eso es un problema muy comon. Windsor construido en Typed Factory Facility le dará los beneficios de usar una fábrica, sin los inconvenientes mencionados.

Cuestiones relacionadas