Questo è in realtà semplice da fare una volta capito che DI riguarda schemi e principi , non la tecnologia.
Per progettare l'API in modo indipendente dal contenitore DI, seguire questi principi generali:
Programma per un'interfaccia, non un'implementazione
Questo principio è in realtà una citazione (dalla memoria) di Design Patterns , ma dovrebbe sempre essere il tuo vero obiettivo . DI è solo un mezzo per raggiungere questo scopo .
Applica il Principio di Hollywood
Il Principio di Hollywood in termini di DI dice: Non chiamare il contenitore DI, ti chiamerà .
Non chiedere mai direttamente una dipendenza chiamando un contenitore dal tuo codice. Chiedilo implicitamente usando l' iniezione del costruttore .
Usa iniezione costruttore
Quando hai bisogno di una dipendenza, richiedila staticamente tramite il costruttore:
public class Service : IService
{
private readonly ISomeDependency dep;
public Service(ISomeDependency dep)
{
if (dep == null)
{
throw new ArgumentNullException("dep");
}
this.dep = dep;
}
public ISomeDependency Dependency
{
get { return this.dep; }
}
}
Nota come la classe di servizio garantisce i suoi invarianti. Una volta creata un'istanza, la dipendenza sarà garantita a causa della combinazione della clausola di guardia e della readonly
parola chiave.
Usa Abstract Factory se hai bisogno di un oggetto di breve durata
Le dipendenze iniettate con l'iniezione del costruttore tendono a durare a lungo, ma a volte è necessario un oggetto di breve durata o per costruire la dipendenza in base a un valore noto solo in fase di esecuzione.
Vedi questo per maggiori informazioni.
Componi solo all'ultimo momento responsabile
Mantieni gli oggetti disaccoppiati fino alla fine. Normalmente, puoi attendere e cablare tutto nel punto di ingresso dell'applicazione. Questa è chiamata radice della composizione .
Maggiori dettagli qui:
Semplifica usando una facciata
Se ritieni che l'API risultante diventi troppo complessa per gli utenti alle prime armi, puoi sempre fornire alcune classi di facciata che incapsulano le comuni combinazioni di dipendenze.
Per fornire una facciata flessibile con un alto grado di rilevabilità, potresti prendere in considerazione la possibilità di fornire fluenti costruttori. Qualcosa come questo:
public class MyFacade
{
private IMyDependency dep;
public MyFacade()
{
this.dep = new DefaultDependency();
}
public MyFacade WithDependency(IMyDependency dependency)
{
this.dep = dependency;
return this;
}
public Foo CreateFoo()
{
return new Foo(this.dep);
}
}
Ciò consentirebbe a un utente di creare un Foo predefinito scrivendo
var foo = new MyFacade().CreateFoo();
Tuttavia, sarebbe molto evidente che è possibile fornire una dipendenza personalizzata e si potrebbe scrivere
var foo = new MyFacade().WithDependency(new CustomDependency()).CreateFoo();
Se immagini che la classe MyFacade incapsuli molte dipendenze diverse, spero sia chiaro come fornire impostazioni predefinite corrette e allo stesso tempo rendere rilevabile l'estensibilità.
FWIW, molto tempo dopo aver scritto questa risposta, ho approfondito i concetti qui e ho scritto un post sul blog più lungo sulle biblioteche DI-Friendly e un post di accompagnamento su DI-Friendly Frameworks .