In realtà, il modo "giusto" è NON usare affatto una fabbrica a meno che non ci sia assolutamente altra scelta (come nei test unitari e certi mock - per il codice di produzione NON si usa una fabbrica)! Fare così è in realtà un anti-modello e dovrebbe essere evitato a tutti i costi. L'intero punto dietro un contenitore DI è consentire al gadget di eseguire il lavoro per .
Come indicato sopra in un post precedente, desideri che il tuo gadget IoC si assuma la responsabilità della creazione dei vari oggetti dipendenti nella tua app. Ciò significa lasciare che il tuo gadget DI crei e gestisca le varie istanze stesse. Questo è il punto dietro DI - i tuoi oggetti non dovrebbero MAI sapere come creare e / o gestire gli oggetti da cui dipendono. Fare altrimenti pause accoppiamento lento.
La conversione di un'applicazione esistente in tutti i DI è un grande passo, ma mettendo da parte le ovvie difficoltà nel fare ciò, vorrai anche (solo per semplificarti la vita) per esplorare uno strumento DI che eseguirà il grosso di i tuoi binding automaticamente (il nucleo di qualcosa come Ninject sono le chiamate "kernel.Bind<someInterface>().To<someConcreteClass>()"
che fai per abbinare le dichiarazioni dell'interfaccia a quelle classi concrete che desideri utilizzare per implementare tali interfacce. Sono quelle chiamate "Bind" che consentono al tuo gadget DI di intercettare il costruttore chiama e fornisce le istanze dell'oggetto dipendente necessarie. Un tipico costruttore (pseudo codice mostrato qui) per alcune classi potrebbe essere:
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);
}
}
Nota che da nessuna parte in quel codice era qualsiasi codice che creava / gestiva / rilasciava l'istanza di SomeConcreteClassA o SomeOtherConcreteClassB. Di fatto, nessuna classe concreta è stata nemmeno citata. Quindi ... dove è avvenuta la magia?
Nella parte di avvio della tua app, ha avuto luogo (di nuovo, questo è pseudo codice ma è abbastanza vicino alla cosa reale (Ninject) ...):
public void StartUp()
{
kernel.Bind<ISomeClassA>().To<SomeConcreteClassA>();
kernel.Bind<ISomeOtherClassB>().To<SomeOtherConcreteClassB>();
}
Un po 'di codice ci dice che il gadget di Ninject cerca i costruttori, li scannerizza, cerca le istanze di interfacce che è stata configurata per gestire (cioè le chiamate "Bind") e quindi crea e sostituisce un'istanza del classe concreta ovunque venga fatta riferimento all'istanza.
C'è un bel tool che integra Ninject molto bene chiamato Ninject.Extensions.Conventions (ancora un altro pacchetto NuGet) che farà la maggior parte di questo lavoro per te. Per non tralasciare l'eccellente esperienza di apprendimento che dovrai affrontare mentre sviluppi questo, ma per iniziare, questo potrebbe essere uno strumento da investigare.
Se la memoria serve, Unity (formalmente da Microsoft ora un progetto Open Source) ha una chiamata al metodo o due che fanno la stessa cosa, altri strumenti hanno assistenti simili.
Qualunque sia il percorso che scegli, leggi sicuramente il libro di Mark Seemann per la maggior parte della tua formazione DI, tuttavia, va sottolineato che anche i "Grandi" del mondo dell'ingegneria del software (come Mark) possono commettere errori evidenti - Mark Ho dimenticato tutto di Ninject nel suo libro, quindi ecco un'altra risorsa scritta proprio per Ninject. Ce l'ho ed è una buona lettura: Padroneggiare Ninject for Dependency Injection