2011-10-21 8 views
6

Hace poco pregunté acerca de cómo hacer DI correctamente y obtuve algunos enlaces a publicaciones en blogs sobre el tema. Creo que entiendo mejor ahora: separar la construcción de objetos de la lógica, poniéndolo en fábricas. Pero todos los ejemplos son para sitios web como, por ejemplo, para hacer todo el cableado al inicio. Llame a una fábrica grande que new s todo y pasa en todas las dependencias.¿Cuándo y dónde llamar a las fábricas en tiempo de ejecución?

Pero, ¿qué pasa si no quiero crear una instancia de todo por adelantado? Tengo un objeto que contiene una lista de otros objetos a los que puede delegar, pero son caros y se usan uno a la vez, por lo que los construyo cuando sea necesario y los dejo compilar cuando termine. No quiero poner new B() dentro de la lógica de A porque prefiero usar DI, pero ¿cómo? ¿Puede A llamar a la fábrica? Eso no parece mucho mejor, a menos que la fábrica mantenga el estado, incluidas las dependencias actuales. Simplemente no quiero pasar la lista completa de B s en A cuando está construida, ya que sería un desperdicio. Si lo desea, B no necesariamente tienen estar dentro A, a pesar de que tiene un sentido lógico (A es un nivel de juego, B es una sola pantalla), pero en cualquier caso la lógica de A dicta cuando se crea B.

Entonces, ¿quién llama a la fábrica para obtener B, y cuándo?

aclaración: No estoy utilizando el marco para DI. Me pregunto si el término DI implica eso?

+0

Muy buena pregunta. Me he sentido confundido por este problema también. Además, a veces quiero múltiples instancias de algo (¡y un número arbitrario de ellas!) Y he estado muy confundido sobre cómo se supone que funciona. Con Ninject lo acabo de chupar e inyecté 'IKernel' pero me dieron a entender que está mal visto. (aunque en mi experiencia, muy poderoso y aún no me arrepiento - y no, ni remotamente estoy interesado en "intercambiar" mi marco DI, por lo que el beneficio es discutible para mí) –

+0

Sólo es desaprobado por los desarrolladores que tener cierta comprensión religiosa de los patrones. Si bien no me volvería loco cuando todo el mundo sabe sobre IKernel, o como se llame en tu estilo de marco DI, a veces es la mejor manera de hacerlo. He hecho esto también, y nada ha explotado en mí. – Andy

Respuesta

6

En Ninject, puede registrar Func<B> y solicitarlo en el constructor al A.

Autofac suministrará automágicamente Func<B> si B ya está registrado.

O, puede tomar el enfoque más directo y definir una fábrica explícita para B, y solicitar esa fábrica en el constructor; es simplemente más tipeo ya que tendrías que crear una fábrica para cada dependencia que quieras inicializar perezosamente.


Aquí hay otra respuesta, así que muestra métodos de fábrica de estilo Ninject: How do I handle classes with static methods with Ninject?


@Not utilizando un marco: Si puede, probablemente me prueba a utilizar una: una IOC/El marco DI usualmente maneja la creación retrasada para usted desde el primer momento.

Si quiere continuar enrollando el suyo, simplemente pase la fábrica que crea B a su objeto A. O bien, si simplemente no te gustan los Funcs sin procesar y no quieres tener que crear fábricas explícitas para todos tus objetos, entonces podrías mirar usando Lazy<B> para una solución más formal.

+1

+1, gran respuesta sensata. – Andy

+0

Guau, lindo! De acuerdo con Andy. –

+0

La solución con la que trabajé no coincide exactamente con ninguna de estas respuestas (utilicé la mayoría de las cosas por adelantado), pero esta me dio algunas ideas nuevas sobre los enfoques de DI, así que lo acepto. – Tesserex

1

Normalmente hay dos patrones para usar objetos raramente necesarios que son caros de crear. El primer patrón es usar una fábrica, como sugiere David Faivre. El otro es mediante el uso de un proxy.

Un proxy es, desde una perspectiva de diseño, probablemente la solución más limpia, aunque podría necesitar más código para implementar. Es el más limpio, porque la aplicación puede ignorarlo por completo, ya que no necesita una interfaz adicional (como lo requiere el enfoque de fábrica).

Aquí se muestra un ejemplo sencillo con un poco de interfaz y una implementación caro:

interface IAmAService 
{ 
    void DoSomething(); 
} 

class ExpensiveImpl : IAmAService 
{ 
    private object x = [some expensive init]; 

    void IAmAService.DoSomething() { } 
} 

No se puede poner en práctica un proxy basado en esa interfaz, que puede retrasar la creación de esa aplicación:

class ServiceProxy : IAmAService 
{ 
    private readonly Func<IAmAService> factory; 
    private IAmAService instance; 

    public ServiceProxy(Func<IAmAService> factory) 
    { 
     this.factory = factory; 
    } 

    void IAmAService.DoSomething() 
    { 
     this.GetInstance().DoSomething(); 
    } 

    private IAmAService GetInstance() 
    { 
     // TODO: Implement double-checked lock only a single 
     // instance may exist per ServiceProxy. 
     if (this.instance == null) 
     { 
      this.instance = this.factory(); 
     } 

     return this.instance; 
    } 
} 

Esta clase proxy acepta un delegado de fábrica como dependencia, tal como lo describió David Faivre en su respuesta, pero de esta manera la aplicación no tendrá que depender del Func<IAmAService>, sino que simplemente puede depender de IAmAService.

Ahora, en lugar de inyectar un ExpensiveImpl, se puede inyectar un ServiceProxy en otros casos:

// Create the proxy 
IAmAService service = 
    new ServiceProxy(() => new ExpensiveImpl()); 

// Inject it into whatever you wish, such as: 
var customerService = new CustomerService(service); 
+1

+1 - Siempre me molestaba que para crear una "fábrica" ​​tuve que crear dos interfaces: una para IService y otra para IServiceFactory, y luego implementaciones para ambas (esta es la razón por la que acabo de usar Func - y porque soy flojo, probablemente continúe haciéndolo). Sin embargo, me gusta el hecho de que está ocultando la fábrica en una implementación directa de IService. Todavía hay dos implementaciones, pero ahora al menos es solo una interfaz. Neeto. –

Cuestiones relacionadas