2011-08-13 15 views
5

Estoy tratando de mejorar con IoC, DI y OOD para una mejor capacidad de prueba y un acoplamiento más flexible.OOD usando contenedores IoC: ¿cómo construir objetos dependientes?

modo que cuando diseñamos clases con un uso intensivo de la COI y DI podemos endup con clases con múltiples dependencias, por ejemplo

class Processor 
{ 
    private IService1 m_Service1; 
    private IService2 m_Service2; 
    private IService3 m_Service3; 

    //approach 1 
    public Processor(IService1 service1, IService2 service2, IService3 service3) 
    { 
     m_Service1 = service1; 
     m_Service2 = service2; 
     m_Service3 = service3; 
    } 
    //approach 2 
    public Processor(IContainer container) 
    { 
     m_Service1 = container.Resolve<IService1>(); 
     m_Service2 = container.Resolve<IService2>(); 
     m_Service3 = container.Resolve<IService3>(); 
    } 

    //approach 3 
    public delegate Processor Factory(); 

} 

Im pensando en lo que debería ser el enfoque habitual aquí. Podemos dejar constructor con 3 parámetros, pero si estamos construyendo aplicación usando autofac (por ejemplo) más probable es que rara vez se utilizarán otras que por resolver los tipos de alguna instancia del contenedor como

Processor proc = new Processor(
container.Resolve<IService1>(), 
container.Resolve<IService2>(), 
container.Resolve<IService3>()); 

así que estoy pensando que tal enfoque 2 es mejor, cuando dependemos de múltiples tipos desde el contenedor. De todos modos, tendremos que agregar una referencia a autofac en algún lugar, por lo que hay razones para no hacerlo ahora?

Autofac también proporciona enfoque método de fábrica delegado

http://code.google.com/p/autofac/wiki/DelegateFactories

var processorFactory = container.Resolve<Processor.Factory>(); 
Processor processor = processorFactory.Invoke(); 

Así también hemos acercarse 3 - no vamos a utilizar constructores para crear nuestras instancias de clases, en lugar estaremos llamando delegado resuelto a partir de contenedores y resolverá las dependencias para nosotros.

Como soy bastante nuevo en IoC es difícil decir cuándo deberíamos usar 1,2,3. Ellos tienen ventajas y desventajas.

Creo que, en general, si la clase tiene 1 dependencia, probablemente siempre podamos usar el enfoque 1 ... aparte de eso, no estoy seguro de qué elegir y cuándo.

ACTUALIZACIÓN He leído sobre el servicio de localizador contra patrón, pero he llegar a cuarto (o verdadera tercera aproximación)

está cerca de ServiceLocator excepto que no es, se pasa un objeto que se parece a esto

public class ServiceLocatorObject 
{ 
     private IService1 m_Service1; 
     private IService2 m_Service2; 
     private IService3 m_Service3; 
     public IService1 Service1 {get {return m_Service1;}} 
     public IService2 Service2 {get {return m_Service2;}} 
     public IService3 Service3 {get {return m_Service3;}} 

     public ServiceLocatorObject(IService1 service1, IService2 service2, IService3 service3) 
     { 
      m_Service1 = service1; 
      m_Service2 = service2; 
      m_Service3 = service3; 
     } 
} 

Y ahora creamos

//approach 4 
public Processor(ServiceLocatorObject servicesToUse) 
{ 
    m_Services = servicesToUse; 
} 

ahora hemos desacoplado nuestra clase de implementaciones de servicios de una Está claro qué dependencias reales necesita (si suponemos que se requieren todos los servicios disponibles en el objeto pasado) porque no estamos pasando un contenedor que puede contener 100 implementaciones. Y ese objeto se puede incluso reutilizar si esa combinación de servicios 3 puede ser requerida en alguna otra clase en nuestra aplicación. Entonces, estamos usando el constructor DI no el patrón ServiceLocator. la interfaz es clara y no está sobrecargada de dependencias, la nueva clase podría ser un buen candidato para la reutilización.

¿Qué dirías sobre este?

Respuesta

6

El patrón de ubicación del servicio a menudo se considera un antipatrón en estos días (utilizando el contenedor. Resolver e inyectar el contenedor).

Después de MUCHO luchando con este concepto y tratando de decidir si me gusta o no, he llegado a la conclusión personal de que acepto que la ubicación del servicio es un antipatrón porque oscurece las interdependencias que existen y que son un concepto central de OOP.

tener una lectura aquí: http://blog.ploeh.dk/2010/02/03/ServiceLocatorIsAnAntiPattern.aspx

hecho, me gusta el hecho de que en la opción 1, Proceso expresa claramente su dependencia de cada uno de los servicios que en él se mencionan son los parámetros en el constructor. Hace las dependencias muy obvias ... y creo que ayuda a promover un buen diseño.

El solo hecho de que el procesador tome un IContainer no nos dice mucho ... y por lo tanto, debe observar de cerca para identificar las interdependencias.

+0

enlace es bueno, ese libro se ve exactamente lo que necesito. ¿Qué piensas sobre el tercer enfoque? –

+0

+1 Consulte mi respuesta para más comentarios. –

+1

Uso el tercer enfoque en todo mi código ... cuando necesito inyectar un medio para crear muchas instancias de un tipo a lo largo del tiempo. Yo uso Windsor ... así que uso mucho las fábricas de delegados e inyecto Func cuando sé que mi procesador necesitará crear muchos IService1s a lo largo del tiempo ... – Jeff

0

No se trata de una cantidad de dependencias ni una decisión por clase. El Método 2 introduce una nueva dependencia, pero si desea confiar en un contenedor IoC, entonces es un buen enfoque. El enfoque 3 es como el segundo, pero vamos a hacer algunas cosas en la fábrica en el futuro. El Método 1 es el más simple, no se basa en nada y se debe usar para dependencias que normalmente no administraría a través del contenedor IoC.

+0

no se trata de varias dependencias, ni una decisión por clase. - podrías elaborar? Entonces, deberíamos elegir un solo enfoque para toda la aplicación y usarlo en todas partes o ¿qué estás diciendo? No estoy seguro de por qué no puede ser una decisión por clase. –

+0

La opción de usar el contenedor IoC afecta a toda la aplicación; cada módulo debe depender de ella. El enfoque 1 es natural: todos los desarrolladores lo entienden. El enfoque 2 es específico del contenedor. Lo peor que puedes hacer es mezclarlos. El lector del código debería verificar qué aplicación se usa cuando, lo mismo vale para el redactor de pruebas. –

6

La respuesta dada por JeffN825 es correcta, pero me gustaría añadir a lo que nunca te crea una nueva instancia del procesador utilizando un recipiente de esta manera:

Processor proc = new Processor(
    container.Resolve<IService1>(), 
    container.Resolve<IService2>(), 
    container.Resolve<IService3>()); 

Más bien, te dejaría entrar el contenedor de auto-alambre las dependencias y resolve it one go:

Processor proc = container.Resolve<Processor>(); 
+0

gracias, Mark. ¿Podrías decir algo sobre el tercer enfoque? ¿Cuáles son las situaciones en las que es mejor que su Procesador sugerido? = Contenedor. Resuelva (); o primer acercamiento? –

+1

El tercer enfoque es solo una fábrica abstracta disfrazada: http://blog.ploeh.dk/2009/05/28/DelegatesAreAnonymousInterfaces.aspx No está realmente relacionado con los otros dos enfoques. ¿Qué harías con eso? –

+0

hola Mark, he agregado una actualización a la pregunta después de leer algunas de tus publicaciones, ¿qué opinas al respecto? –

Cuestiones relacionadas