2011-08-04 19 views
16

¿Cómo combino la inyección del constructor con los parámetros del constructor "manual"? es decir.¿Combinación de DI con parámetros de constructor?

public class SomeObject 
{ 
    public SomeObject(IService service, float someValue) 
    { 
    } 
} 

Donde IService debe ser resuelto/inyectado por mi contenedor DI, y se debe especificar someValue. ¿Cómo mezclo los dos?

+0

Por parámetros de constructor manual, ¿quiere decir cuando está construyendo manualmente la clase en lugar de DI o quiere decir que el contenedor DI pasa en un parámetro? Si es el primero, ¿podrías hacer una sobrecarga de constructor? – lahsrah

+0

En cualquier caso, siempre necesito IService, así que supongo que no podría hacer una sobrecarga, sin las dependencias y mágicamente usa el constructor completo (a menos que use ServiceLocator - ¡asco!). –

Respuesta

13

Dichas construcciones deben evitarse siempre que sea posible. Por lo tanto, pregúntate: ¿este parámetro es realmente necesario como argumento de constructor? ¿O puede SomeObject ser reemplazado por uno sin estado que es reutilizado por todos los que dependen de él pasando el parámetro al método que ejecuta en el objeto?

p. Ej.En lugar de

public class SomeObject 
{ 
    private float someValue 
    public SomeObject(IService service, float someValue) 
    { 
     this.someValue = someValue 
    } 

    public float Do(float x) 
    { 
     return this.Service.Get(this.someValue) * x; 
    } 
} 

uso

public class SomeObject 
{ 
    public SomeObject(IService service) 
    { 
    } 

    public float Do(float x, float someValue) 
    { 
     return this.Service.Get(someValue) * x; 
    } 
} 

Si es necesario ir a una fábrica:

public interface ISomeObjectFactory 
{ 
    ISomeObject CreateSomeObject(float someValue); 
} 

public class SomeObjectFactory : ISomeObjectFactory 
{ 
    private IKernel kernel; 
    public SomeObjectFactory(IKernel kernel) 
    { 
     this.Kernel = kernel; 
    } 

    public ISomeObject Create(float someValue) 
    { 
     return this.kernel.Get<ISomeObject>(WithConstructorArgument("someValue", someValue); 
    } 
} 

Vista previa: Ninject 2.4 no requerirá de la implementación más, pero permitirá

kernel.Bind<ISomeObjectFactory>().ToFactory(); // or maybe .AsFactory(); 
+0

+1 Definitivamente evitar es la respuesta correcta, edité mi respuesta para estar de acuerdo. Sé que ocultaría un poco el punto, pero como ya se mencionó en otro lugar, considere expresar su SomeObjectFactory con un Func ctor arg en lugar de usar Kernel directamente –

+0

@Ruben Bartelink: Acabo de obtener esta versión porque creo que será la preferida para Ninject 2.4. La fábrica será generada automáticamente por el kernel. La variante de Func también se admitirá con la desventaja de que Func no proporciona información sobre los parámetros y hace que la coincidencia de parámetros sea más difícil. Por lo tanto, ya no me quedaría con la variante de Func para poder actualizar simplemente borrando la implementación de fábrica y cambiando el enlace. Pero también pondría la fábrica en el arranque mientras que la interfaz está en el lado del consumidor. –

+0

Gracias por explicar las sutilezas de la inyección Func, no lo había pensado. Simplemente estaba haciendo un punto que en su mano-impl de la fábrica [que 2.4 se autogenerará], está tomando una dep dep en el núcleo que podría eliminarse tomando una función en su lugar. Pensando en ello ahora, hacerlo contestar menos claro y no asignar lo que 'ToFactory' va a hacer. Así que olvida lo que dije y ¡gracias por explicarme! Y es su_gg_est: P BTW ha escrito mal el nombre del método como Create (no CreateSomeObject). Yo personalmente usaré Func de preferencia ya que vivo en un mundo ofuscado ... –

1

Si 'somevalue' es siempre constante, entonces se puede pensar en el uso de InjectionParameters mientras estás registrar su tipo con el contenedor, ya que se explica en el siguiente mensaje

See Here

pero si eso no es verdad, que no hay forma de separar un valor de parámetro mientras se resuelve una instancia, puede pensar en mover el 'algún Valor' del constructor y convertirlo en una propiedad de la clase.

0

Probablemente usaría una solución ingenua para esto. Si conoce el valor de someValue cuando lo necesite, lo eliminaría del constructor y agregaría una propiedad a su objeto para que pueda establecer someValue. De esta forma, puede obtener su objeto de su contenedor y luego establecer el valor cuando tenga el objeto.

Mi otra sugerencia es que, en lugar de acceder directamente a ella, cree una fábrica que pueda usar para crear dicho objeto. Luego registra la fábrica en su contenedor y utiliza la fábrica para crear su instancia. Algo como esto:

public class SomeObjectFactory : ISomeObjectFactory 
{ 
    private IYourService _service; 
    public SomeObjectFactory(IYourService service) 
    { 
     _service = service; 
    } 

    public ISomeObject Create(float someValue) 
    { 
     return new SomeObject(_service, someValue); 
    } 
} 

puede probar un patrón así.

ACTUALIZACIÓN: Actualizó el código para reflejar los comentarios de mejora.

+0

Parece que tiene una dependencia fuerte en el contenedor de su elección en 'SomeObjectFactory'. Esto no es ampliamente recomendado. En su lugar, inyecte la instancia 'IYourService' en el constructor' SomeObjectFactory' y deje que el conatiner resuelva esa dependencia en la raíz de la composición. –

+0

Por supuesto, mi código es solo un esquema, no es la implementación final. El punto es que debería usar una fábrica en su lugar. Pero actualicé el código de acuerdo con lo que creo que quisiste decir. –

+0

Mucho mejor! :) –

1

Realmente no debería intentar usar D.I. para esto. Podrías encontrar todo tipo de soluciones alocadas, pero pueden no tener sentido en el futuro.

Nuestro enfoque es crear una fábrica a través de D.I., y el método de creación de la fábrica se construiría utilizando el aprobado D.I. envase. No es necesario que utilicemos este patrón a menudo, pero cuando lo hacemos realmente hace que el producto sea mucho más limpio (ya que hace que nuestros gráficos de dependencia sean más pequeños).

1

En NInject, con el que ha etiquetado esto, inyecta una fábrica generada automáticamente en forma de Func<parameters you wish to feed in,T>, utilizando FuncModule as described in this post.

Este enfoque también está disponible en autofac para uno.

Varios Factory method approaches are covered in the answers to this question.

EDIT: NB Si bien esto puede ser entretenido, por favor utilice la solución de @Remo Gloor (y críticamente el consejo re evitando una solución de esta naturaleza)

Cuestiones relacionadas