En realidad, la forma "correcta" es NO usar una fábrica en absoluto a menos que no haya absolutamente otra opción (como en las pruebas unitarias y ciertos simulacros, ¡para el código de producción NO se usa una fábrica)! Hacerlo es en realidad un antipatrón y debe evitarse a toda costa. El objetivo detrás de un contenedor DI es permitir que el dispositivo haga el trabajo por usted.
Como se indicó anteriormente en una publicación anterior, desea que su dispositivo IoC asuma la responsabilidad de la creación de los diversos objetos dependientes en su aplicación. Eso significa permitir que su dispositivo DI cree y administre las distintas instancias. Este es todo el punto detrás de DI: sus objetos NUNCA deben saber cómo crear y / o administrar los objetos de los que dependen. Hacer lo contrario rompe el acoplamiento flojo.
La conversión de una aplicación existente a todos los DI es un gran paso, pero dejando de lado las dificultades obvias para hacerlo, también querrá (solo para hacer su vida un poco más fácil) explorar una herramienta DI que realizará la mayor parte de sus enlaces automáticamente (el núcleo de algo como Ninject son las "kernel.Bind<someInterface>().To<someConcreteClass>()"
llamadas que realiza para hacer coincidir sus declaraciones de interfaz con las clases concretas que desea utilizar para implementar esas interfaces. Son esas llamadas "Bind" las que permiten que su dispositivo DI intercepte sus llamadas de constructor y proporcione instancias de objeto dependiente necesarias Un constructor típico (pseudocódigo mostrado aquí) para alguna clase podría ser:
public class SomeClass
{
private ISomeClassA _ClassA;
private ISomeOtherClassB _ClassB;
public SomeClass(ISomeClassA aInstanceOfA, ISomeOtherClassB aInstanceOfB)
{
if (aInstanceOfA == null)
throw new NullArgumentException();
if (aInstanceOfB == null)
throw new NullArgumentException();
_ClassA = aInstanceOfA;
_ClassB = aInstanceOfB;
}
public void DoSomething()
{
_ClassA.PerformSomeAction();
_ClassB.PerformSomeOtherActionUsingTheInstanceOfClassA(_ClassA);
}
}
Tenga en cuenta que en ningún lugar de ese código había ningún código que creara / administrara / liberara la instancia de SomeConcreteClassA o SomeOtherConcreteClassB. De hecho, ninguna clase concreta fue referenciada. Entonces ... ¿dónde sucedió la magia?
En la parte de inicio de su aplicación, ocurrió lo siguiente (de nuevo, este es un pseudocódigo pero está bastante cerca de lo real (Ninject) ...):
public void StartUp()
{
kernel.Bind<ISomeClassA>().To<SomeConcreteClassA>();
kernel.Bind<ISomeOtherClassB>().To<SomeOtherConcreteClassB>();
}
Ese pequeño fragmento de código le dice al dispositivo Ninject que busque constructores, los escanee, busque instancias de interfaces que haya sido configurado para manejar (esas son las llamadas "Bind") y luego cree y sustituya una instancia de la clase concreta donde sea Se hace referencia a la instancia.
Hay una buena herramienta que complementa muy bien a Ninject llamada Ninject.Extensions.Conventions (otro paquete NuGet) que hará la mayor parte de este trabajo por usted. No para alejarse de la excelente experiencia de aprendizaje que atravesará a medida que lo desarrolle usted mismo, pero para comenzar, esta podría ser una herramienta para investigar.
Si la memoria funciona, Unity (formalmente de Microsoft ahora un proyecto de código abierto) tiene una llamada al método o dos que hacen lo mismo, otras herramientas tienen ayudantes similares.
Sea cual sea el camino que elija, definitivamente lea el libro de Mark Seemann para la mayor parte de su formación DI, sin embargo, debe señalarse que incluso los "Grandes" del mundo de la ingeniería de software (como Mark) pueden cometer errores evidentes: Mark se olvidó por completo de Ninject en su libro, así que aquí hay otro recurso escrito solo para Ninject. Lo tengo y es una buena lectura: Dominar Ninject para inyección de dependencia