15

Ho studiato IoC, Dipendenza iniezione ecc. E mi sono goduto il processo. I vantaggi del disaccoppiamento e della programmazione delle interfacce sono, per me, un gioco da ragazzi.Come disaccoppiare l'implementazione di IoC Framework

Tuttavia, non mi piace davvero legarmi a un framework specifico come Unity o Autofac o Windsor - perché sto ancora imparando e non ho ancora deciso quale sia il migliore per i miei scopi.

Quindi, come posso avvolgere qualcosa come Unity in modo da potermi facilmente scambiare a Windsor in un secondo momento? (o qualsiasi altra cosa). E non osare dire usarne un altro per iniettare il primo;)

Grazie!

R.

P.s Ho taggato Unity in quanto è la mia attuale preferenza personale (adoro solo Entlib).

risposta

18

Si può certamente provare a fare un'astrazione dal contenitore dichiarando un IContainer con dire Resolve e Register. L'ho fatto un paio di volte. Quindi procedere e implementare un contenitore : IContainer e incapsulare un contenitore IoC effettivo con l'astrazione. L'ho provato con Unity e Castle Windsor.

Ma hey, presto mi sono reso conto che si trattava di una vera e propria ingegneria. Ho poi capito che ho cercato di astrarre dall'astrazione, eppure di costruire un'altra astrazione. Questo potrebbe andar bene per imparare il concetto, ma è stato un vero dolore al collo in un vero progetto. Consiglio vivamente contro un'astrazione dal contenitore IoC. Se si utilizza correttamente il principio DI, sarà comunque abbastanza semplice cambiare il contenitore.

Il codice sembra troppo complicata, come

//I did this mess with Service Locator 
var t = ContainerService.Instance.Resolve<IMyType>(); 
//others could go further with same Service Locator 
var t = IoCFactory.Instance.CurrentContainer.Resolve<IMyType>(); 

//better way, use --> IoC and DI <-- 
//when a program starts, or a new instance of the context created 
var t = Container.Resolve<IMyType>() //this lives at the bottom of the stack 
//and then you just pass IMyType to the constructor of other types  
//you don't need to call Resolve again in the logical cycle 

Vedi this post da Ayende.

Sì, hanno estratto il contenitore Inversion of Control. Penso che se hai bisogno di farlo, è abbastanza chiaro che lo non ottieni realmente ciò di cui IoC fa parte.

+0

buon punto, astrazione IoC suona come overdesign – pkmiec

+0

Hai perfettamente ragione, questo è per i requisiti di apprendimento. Una volta che mi sistemerò sul mio container de jour, sarei felice. Ma quando stai imparando e devi riscrivere le app per gestire un altro contenitore perché l'esempio su un blog sta usando x invece di y, beh, ti arriva dopo un po '. Ho pensato che sarebbe stato carino avere il mio esempio e trovare un modo per scambiare facilmente qualsiasi contenitore utilizzato da un blog per illustrare un particolare concetto. – Richard

+0

Sono d'accordo, questo è eccessivo. Un contenitore IoC decente dovrebbe avere un sacco di spazio per la configurazione in modo da non dover inquinare il codice con i riferimenti al contenitore. Se l'astrazione del contenitore è l'unico modo per farlo funzionare, direi che stai utilizzando un contenitore IoC scadente. – FMM

3

Controlla la libreria Common Service Locator.

+6

imho, CSL è davvero utile solo quando si crea un componente che verrà utilizzato in più progetti disparati che potrebbero scegliere di utilizzare librerie IoC diverse. Ad esempio, l'utilizzo di CSL nella propria app line-of-business, ad esempio, è eccessivo. Aggiunge molto nella gestione delle dipendenze e fornisce solo un'interfaccia molto stretta che potrebbe semplicemente essere specifica in una infrastruttura core della stessa app. –

+3

Come uno dei progettisti di CSL, sostengo fermamente il commento sopra. CSL è destinato agli autori di librerie che desiderano un contenitore, ma non vogliono forzare la scelta del contenitore della libreria sull'app. –

18

Utilizzare l'iniezione del costruttore per comunicare le dipendenze richieste da una classe. Ogni contenitore che hai elencato lo supporta.

A volte un pezzo di codice non può raggiungere l'indipendenza completa del contenitore, ma questi casi dovrebbero essere una parte molto piccola del codice base.

+5

Assolutamente giusto per preferire l'iniezione del costruttore. – FMM

4

Come hanno già detto altre persone, preferisco l'iniezione del costruttore. Questo risolverà molti dei tuoi problemi.

Se le classi hanno dipendenze dirette sul contenitore IoC stesso, tende ad essere una variante dell'utilizzo del modello di localizzazione del servizio (anti-). In questo caso particolare, isolare i tipi che vengono risolti tramite il localizzatore di servizio e astrarre tale risoluzione dinamica con un'interfaccia di fabbrica.Così, per esempio, sostituire questo:

public class Foo 
{ 
    private MyIoCContainer _container; 

    public Foo(MyIoCContainer container) 
    { 
     this._container = container; 
    } 


    public void DoSomething() 
    { 
     // have to do this at runtime for whatever reason 
     var myObj = this._container.Resolve<ISomeType>(); 

     myObj.DoSomething(); 
     myObj.DoSomethingElse(); 
    } 
} 

con questo:

public class Foo 
{ 
    private IObjFactory _provider; 

    public Foo(IObjFactory _provider) 
    { 
     this._provider = provider; 
    } 


    public void DoSomething() 
    { 
     var myObj = _provider.GetObj(); 

     myObj.DoSomething(); 
     myObj.DoSomethingElse(); 
    } 
} 

public interface IObjFactory 
{ 
    ISomeType GetObj(); 
} 

Ora, si ha un IObjFactory in grado di incapsulare la dinamica, la natura runtime di costruire oggetti che implementano ISomeType. Se stai costruendo molti diversi tipi di oggetti dal container/localizzatore di servizi, allora dovresti avere almeno altrettante interfacce *Factory (in conformità con lo Interface Segregation Principle).

9

Un contenitore DI deve essere referenziato solo dallo Composition Root. Tutti gli altri moduli non devono avere alcun riferimento al contenitore.

-Mark Seemann (autore di Dependency Injection in .NET)

In altre parole, si dovrebbe solo bisogno di cambiare una classe se si cambia contenitori DI.

L'iniezione del costruttore è normalmente la strada giusta da percorrere, come altri hanno menzionato. È possibile iniettare interfacce di fabbrica o delegati Func<T> se è necessario creare oggetti al volo.

Vorrei anche suggerire di evitare la configurazione XML quando possibile.

Problemi correlati