patron inyeccion injection explicacion dependency dependencias control dependency-injection inversion-of-control unity-container ioc-container interface-design

dependency-injection - injection - inyeccion de dependencias spring



¿Hay un patrón para inicializar objetos creados a través de un contenedor DI? (5)

Creo que lo resolví y se siente bastante saludable, por lo que debe ser a la mitad :)

IMyIntf en una IMyIntf "getter" y "setter". Asi que:

interface IMyIntf { string RunTimeParam { get; } } interface IMyIntfSetter { void Initialize(string runTimeParam); IMyIntf MyIntf {get; } }

Luego la implementación:

class MyIntfImpl : IMyIntf, IMyIntfSetter { string _runTimeParam; void Initialize(string runTimeParam) { _runTimeParam = runTimeParam; } string RunTimeParam { get; } IMyIntf MyIntf {get {return this;} } } //Unity configuration: //Only the setter is mapped to the implementation. container.RegisterType<IMyIntfSetter, MyIntfImpl>(); //To retrieve an instance of IMyIntf: //1. create the setter IMyIntfSetter setter = container.Resolve<IMyIntfSetter>(); //2. Init it setter.Initialize("someparam"); //3. Use the IMyIntf accessor IMyIntf intf = setter.MyIntf;

IMyIntfSetter.Initialize() todavía se puede IMyIntfSetter.Initialize() varias veces, pero usando bits del paradigma de Localizador de servicios podemos envolverlo bastante bien para que IMyIntfSetter sea ​​casi una interfaz interna distinta de IMyIntf .

Estoy intentando que Unity administre la creación de mis objetos y quiero tener algunos parámetros de inicialización que no se conocen hasta el tiempo de ejecución:

Por el momento, la única forma en que podría pensar en la forma de hacerlo es tener un método Init en la interfaz.

interface IMyIntf { void Initialize(string runTimeParam); string RunTimeParam { get; } }

Entonces para usarlo (en Unity) haría esto:

var IMyIntf = unityContainer.Resolve<IMyIntf>(); IMyIntf.Initialize("somevalue");

En este escenario, runTimeParam param se determina en tiempo de ejecución en función de la entrada del usuario. El caso trivial aquí simplemente devuelve el valor de runTimeParam pero en realidad el parámetro será algo así como el nombre del archivo y el método de inicialización hará algo con el archivo.

Esto crea una serie de problemas, a saber, que el método Initialize está disponible en la interfaz y se puede llamar varias veces. Establecer una bandera en la implementación y lanzar una excepción en una llamada repetida para Initialize parece demasiado complicado.

En el momento en que resuelvo mi interfaz, no quiero saber nada sobre la implementación de IMyIntf . Lo que sí quiero, sin embargo, es el conocimiento de que esta interfaz necesita ciertos parámetros de inicialización de una sola vez. ¿Hay alguna forma de anotar (atributos) la interfaz con esta información y pasarla al marco cuando se crea el objeto?

Editar: describió la interfaz un poco más.


En cualquier lugar donde necesite un valor de tiempo de ejecución para construir una dependencia particular, Abstract Factory es la solución.

Tener Initialize métodos en las interfaces huele a Leaky Abstraction .

En su caso, diría que debe modelar la interfaz IMyIntf sobre cómo debe usarla, y no cómo IMyIntf crear implementaciones de la misma. Eso es un detalle de implementación.

Por lo tanto, la interfaz debería ser simplemente:

public interface IMyIntf { string RunTimeParam { get; } }

Ahora define la fábrica abstracta:

public interface IMyIntfFactory { IMyIntf Create(string runTimeParam); }

Ahora puede crear una implementación concreta de IMyIntfFactory que cree instancias concretas de IMyIntf como esta:

public class MyIntf : IMyIntf { private readonly string runTimeParam; public MyIntf(string runTimeParam) { if(runTimeParam == null) { throw new ArgumentNullException("runTimeParam"); } this.runTimeParam = runTimeParam; } public string RunTimeParam { get { return this.runTimeParam; } } }

Observe cómo esto nos permite proteger las invariantes de la clase mediante el uso de la palabra clave readonly . No se necesitan métodos de inicialización malolientes.

Una implementación de IMyIntfFactory puede ser tan simple como esto:

public class MyIntfFactory : IMyIntfFactory { public IMyIntf Create(string runTimeParam) { return new MyIntf(runTimeParam); } }

En todos sus consumidores donde necesita una instancia IMyIntf , simplemente toma una dependencia de IMyIntfFactory solicitándolo a través de Constructor Injection .

Cualquier contenedor DI que valga la pena será capaz de IMyIntfFactory automáticamente una instancia de IMyIntfFactory si la registra correctamente.


No puedo responder con la terminología específica de Unity, pero parece que estás aprendiendo sobre la inyección de dependencia. Si es así, le pido que lea la guía breve, clara y llena de información del usuario para Ninject .

Esto lo guiará a través de las diversas opciones que tiene al usar DI, y cómo dar cuenta de los problemas específicos que enfrentará en el camino. En su caso, lo más probable es que desee utilizar el contenedor DI para crear instancias de sus objetos, y hacer que ese objeto obtenga una referencia a cada una de sus dependencias a través del constructor.

El tutorial también detalla cómo anotar métodos, propiedades e incluso parámetros utilizando atributos para distinguirlos en el tiempo de ejecución.

Incluso si no usas Ninject, el tutorial te dará los conceptos y la terminología de la funcionalidad que se adapte a tu propósito, y deberías ser capaz de asignar ese conocimiento a Unity u otros marcos DI (o convencerte para que pruebes Ninject) .


Por lo general, cuando se encuentra con esta situación, necesita volver a visitar su diseño y determinar si está mezclando sus objetos con estado / datos con sus servicios puros. En la mayoría de los casos (no todos), querrá mantener separados estos dos tipos de objetos.

Si necesita un parámetro específico del contexto pasado en el constructor, una opción es crear una fábrica que resuelva sus dependencias de servicio a través del constructor, y tome su parámetro de tiempo de ejecución como un parámetro del método Create () (o Generar ( ), Build () o como quiera que nombre sus métodos de fábrica).

En general, se considera que los setters o el método Initialize () son un mal diseño, ya que debe "recordar" llamarlos y asegurarse de que no abren demasiado el estado de su implementación (es decir, qué es lo que impide que alguien vuelva). -llamar inicializar o el colocador?).


También me he encontrado con esta situación varias veces en entornos en los que estoy creando dinámicamente objetos ViewModel basados ​​en objetos Model (descritos muy bien por esta otra publicación de ).

Me gustó cómo la extensión Ninject que te permite crear fábricas dinámicamente basadas en interfaces:

Bind<IMyFactory>().ToFactory();

No pude encontrar ninguna funcionalidad similar directamente en Unity ; así que escribí mi propia extensión en IUnityContainer, que le permite registrar fábricas que crearán nuevos objetos basados ​​en datos de objetos existentes, mapeando esencialmente desde una jerarquía de tipos a una jerarquía de tipos diferente: UnityMappingFactory@GitHub

Con un objetivo de simplicidad y legibilidad, terminé con una extensión que le permite especificar directamente las asignaciones sin declarar clases o interfaces de fábrica individuales (un ahorro de tiempo real). Simplemente agrega las asignaciones justo donde registras las clases durante el proceso de arranque normal ...

//make sure to register the output... container.RegisterType<IImageWidgetViewModel, ImageWidgetViewModel>(); container.RegisterType<ITextWidgetViewModel, TextWidgetViewModel>(); //define the mapping between different class hierarchies... container.RegisterFactory<IWidget, IWidgetViewModel>() .AddMap<IImageWidget, IImageWidgetViewModel>() .AddMap<ITextWidget, ITextWidgetViewModel>();

Entonces usted declara la interfaz de fábrica de mapeo en el constructor para CI y usa su método Create () ...

public ImageWidgetViewModel(IImageWidget widget, IAnotherDependency d) { } public TextWidgetViewModel(ITextWidget widget) { } public ContainerViewModel(object data, IFactory<IWidget, IWidgetViewModel> factory) { IList<IWidgetViewModel> children = new List<IWidgetViewModel>(); foreach (IWidget w in data.Widgets) children.Add(factory.Create(w)); }

Como una ventaja adicional, cualquier dependencia adicional en el constructor de las clases mapeadas también se resolverá durante la creación del objeto.

Obviamente, esto no resolverá todos los problemas, pero me ha servido bastante bien hasta ahora, así que pensé que debería compartirlo. Hay más documentación en el sitio del proyecto en GitHub.