Estoy intentando que Unity gestione 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 puedo pensar en hacerlo es tener un método Init en la interfaz.
interface IMyIntf {
void Initialize(string runTimeParam);
string RunTimeParam { get; }
}
Luego, para usarlo (en Unity), haría esto:
var IMyIntf = unityContainer.Resolve<IMyIntf>();
IMyIntf.Initialize("somevalue");
En este escenario, el runTimeParam
parámetro se determina en tiempo de ejecución según 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 Initialize
método está disponible en la interfaz y se puede llamar varias veces. Establecer una marca en la implementación y lanzar una excepción en llamadas repetidas Initialize
parece torpe.
En el punto donde resuelvo mi interfaz, no quiero saber nada sobre la implementación de IMyIntf
. Sin embargo, lo que sí quiero es saber que esta interfaz necesita ciertos parámetros de inicialización únicos. ¿Hay alguna manera de anotar de alguna manera (atributos) la interfaz con esta información y pasarlos al marco cuando se crea el objeto?
Editar: describió la interfaz un poco más.
runTimeParam
es una dependencia que se determina en tiempo de ejecución en función de una entrada del usuario. ¿La alternativa para esto debería ser dividirlo en dos interfaces: una para la inicialización y otra para almacenar valores?Respuestas:
En cualquier lugar donde necesite un valor de tiempo de ejecución para construir una dependencia particular, Abstract Factory es la solución.
Tener los métodos Initialize en las interfaces huele a una abstracción permeable .
En su caso, diría que debe modelar la
IMyIntf
interfaz según cómo necesite usarla , no cómo intenta crear implementaciones de la misma. Ese es un detalle de implementación.Por lo tanto, la interfaz debería ser simplemente:
Ahora defina la Fábrica abstracta:
Ahora puede crear una implementación concreta
IMyIntfFactory
que crea instancias concretasIMyIntf
como esta:Observe cómo esto nos permite proteger las invariantes de la clase mediante el uso de la
readonly
palabra clave. No son necesarios métodos de inicialización malolientes.Una
IMyIntfFactory
implementación puede ser tan simple como esto:En todos sus consumidores donde necesita una
IMyIntf
instancia, simplemente dependeIMyIntfFactory
de ella solicitándola a través de la inyección de constructor .Cualquier contenedor DI que valga la pena será capaz de conectar automáticamente una
IMyIntfFactory
instancia por usted si la registra correctamente.fuente
MyIntf
implementación por parte de la fábrica requiere más derunTimeParam
(lea: otros servicios que uno desearía que se resolviera mediante un IoC), entonces todavía tiene que resolver esas dependencias en su fábrica. Me gusta la respuesta de @PhilSandler de pasar esas dependencias al constructor de la fábrica para resolver esto, ¿es esa tu opinión también?Por lo general, cuando se encuentra con esta situación, debe volver a visitar su diseño y determinar si está mezclando sus objetos de estado / datos con sus servicios puros. En la mayoría de los casos (no en todos), querrá mantener estos dos tipos de objetos separados.
Si necesita un parámetro específico de 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 Generate ( ), Build () o como quieras nombrar tus métodos de fábrica).
Tener un método initialize () definidores o están por lo general piensa que es un mal diseño, ya que es necesario "recordar" a llamarlos y asegurarse de que no se abren demasiado de estado de su aplicación (es decir, lo que es dejar a alguien de re -invocando initialize o el setter?).
fuente
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 en esta otra publicación de Stackoverflow ).
Me gustó cómo la extensión Ninject que te permite crear dinámicamente fábricas basadas en interfaces:
Bind<IMyFactory>().ToFactory();
No pude encontrar ninguna funcionalidad similar directamente en Unity ; así que escribí mi propia extensión al IUnityContainer, que le permite registrar fábricas que crearán nuevos objetos basados en datos de objetos existentes, esencialmente asignando 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 agregue las asignaciones justo donde registra las clases durante el proceso normal de arranque ...
Luego simplemente declara la interfaz de fábrica de mapeo en el constructor para CI y usa su método Create () ...
Como beneficio adicional, cualquier dependencia adicional en el constructor de las clases asignadas también se resolverá durante la creación del objeto.
Obviamente, esto no resolverá todos los problemas, pero hasta ahora me ha servido bastante bien, así que pensé que debería compartirlo. Hay más documentación en el sitio del proyecto en GitHub.
fuente
No puedo responder con la terminología específica de Unity, pero parece que estás aprendiendo acerca de la inyección de dependencia. Si es así, le insto a que lea la guía del usuario breve, clara y llena de información para Ninject .
Esto lo guiará a través de las diversas opciones que tiene cuando usa DI, y cómo explicar 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 tiempo de ejecución.
Incluso si no usa Ninject, el tutorial le dará los conceptos y la terminología de la funcionalidad que se adapte a su propósito, y debería poder asignar ese conocimiento a Unity u otros marcos de DI (o convencerlo de que pruebe Ninject) .
fuente
Creo que lo resolví y se siente bastante saludable, por lo que debe ser medio correcto :))
Me dividí
IMyIntf
en interfaces "getter" y "setter". Entonces:Luego la implementación:
IMyIntfSetter.Initialize()
todavía se puede llamar varias veces, pero usando bits del paradigma del Localizador de servicios podemos resumirlo bastante bien, de modo queIMyIntfSetter
es casi una interfaz interna que es distintaIMyIntf
.fuente