2009-05-23 8 views
13

Uso l'iniezione della dipendenza tramite parametri e costruttori in modo estensivo. Capisco il principio fino a questo punto e ne sono felice. Nei miei progetti di grandi dimensioni, finisco con l'iniettare troppe dipendenze (tutto ciò che colpisce le cifre doppie sembra troppo grande - mi piace il termine "codice macaroni").Qualcuno può spiegarmi, a lungo, come utilizzare i contenitori IOC?

Come tale, ho preso in considerazione i contenitori IOC. Ho letto alcuni articoli su di loro e finora non ho visto il beneficio. Riesco a vedere come assiste nell'invio di gruppi di oggetti correlati o nell'ottenere sempre lo stesso tipo. Non sono sicuro di come potrebbero aiutarmi nei miei progetti in cui potrei avere più di cento classi che implementano la stessa interfaccia e dove li uso tutti in ordini diversi.

Quindi, qualcuno può indicarmi alcuni buoni articoli che descrivono non solo i concetti di contenitori IOC (preferibilmente senza esagerarne uno in particolare), ma mostrano anche in dettaglio in che modo mi danno beneficio in questo tipo di progetto e come si adattano nell'ambito di una grande architettura?

Spero di vedere alcune cose non linguistiche ma la mia lingua preferita, se necessario, è C#.

risposta

1

non ho link, ma in grado di fornire un esempio:

Si dispone di un controller web che ha bisogno di chiamare un servizio che ha un livello di accesso ai dati.

Ora, lo prendo nel tuo codice stai costruendo questi oggetti te stesso al momento della compilazione. Stai usando un modello di progettazione decente, ma se hai mai bisogno di cambiare l'implementazione di dire il dao, devi entrare nel tuo codice e rimuovere il codice che imposta questa dipendenza, ricompilare/testare/distribuire. Ma se si dovesse utilizzare un container IOC si cambierebbe semplicemente la classe nella configurazione e si riavvierà l'applicazione.

3

Se si dispone di più di un centinaio di classi che implementano un'interfaccia comune, un CIO non aiuterà molto, avete bisogno di una fabbrica . In questo modo, è possibile effettuare le seguenti operazioni:

public interface IMyInterface{ 
    //... 
} 

public class Factory{ 

    public static IMyInterface GetObject(string param){ 
     // param is a parameter that will help the Factory decide what object to return 
     // (that is only an example, there may not be any parameter at all) 
    } 
} 

//... 
// You do not depend on a particular implementation here 
IMyInterface obj = Factory.GetObject("some param"); 

All'interno della fabbrica, è possibile utilizzare un IoC Container per recuperare gli oggetti, se volete, ma si dovrà registrare ogni una delle classi che implementano il data l'interfaccia e associarli ad alcuni tasti (e usare quelle chiavi come parametri nel metodo GetObject()).

Un CIO è particolarmente utile quando si deve recuperare gli oggetti che implementano interfacce differenti:

IMyInteface myObject = Container.GetObject<IMyInterface>(); 
IMyOtherInterface myOtherObject Container.GetObject<IMyOtherInterface>(); 
ISomeOtherInterface someOtherObject = Container.GetObject<ISomeOtherInterface>(); 

Vedi? Solo un oggetto per ottenere diversi oggetti di tipo diverso e nessuna chiave (gli intefaccia stessi sono le chiavi). Se hai bisogno di un oggetto per ottenere diversi oggetti diversi, ma tutti implementano la stessa interfaccia, un IoC non ti aiuterà molto.

3

Nelle scorse settimane, ho preso il tuffo dall'iniezione di dipendenza solo alla completa inversione del controllo con Castle, quindi capisco da dove viene la tua domanda.

Alcuni motivi per cui non vorrei usare un contenitore IOC:

  1. E 'un piccolo progetto che non è destinato a crescere più di tanto. Se esiste una relazione 1: 1 tra costruttori e chiamate a quei costruttori, l'utilizzo di un contenitore IOC non riduce la quantità di codice che devo scrivere. Non stai violando "non ripeterti" finché non ti ritrovi a copiare e incollare lo stesso identico "var myObject = new MyClass (someInjectedDependency)" per la seconda volta.

  2. Potrebbe essere necessario adattare il codice esistente per facilitare il caricamento nei contenitori IOC. Questo probabilmente non è necessario fino a quando non entri in alcune delle più interessanti funzionalità di programmazione orientate ad Aspect, ma se hai dimenticato di rendere un metodo virtuale, blocca la classe di quel metodo, e non implementa un'interfaccia, e tu? a disagio nel fare quei cambiamenti a causa delle dipendenze esistenti, quindi rendere il passaggio non è altrettanto allettante.

  3. Aggiunge un'ulteriore dipendenza esterna al mio progetto e al mio team. Posso convincere il resto della mia squadra che strutturare il loro codice per permettere a DI di crescere, ma attualmente sono l'unico che sa come lavorare con Castle. Su progetti più piccoli e meno complicati, questo non sarà un problema. Per i progetti più grandi (che, ironia della sorte, trarrebbero il massimo beneficio dai contenitori del CIO), se non posso evangelizzare usando un contenitore del CIO abbastanza bene, fare il maverick sulla mia squadra non aiuterà nessuno.

Alcuni dei motivi per cui non vorrei tornare a DI pianura:

  1. posso aggiungere o togliere la registrazione a qualsiasi numero delle mie classi, senza l'aggiunta di alcun tipo di traccia o dichiarazione di registrazione. Avere la capacità di intrecciare le mie classi con la funzionalità aggiuntiva senza modificare tali classi, è estremamente potente. Per esempio:

    Logging: http://ayende.com/Blog/archive/2008/07/31/Logging--the-AOP-way.aspx

    Transactions: http://www.codeproject.com/KB/architecture/introducingcastle.aspx (saltare fino alla sezione di transazione)

  2. Castello, almeno, è così utile durante il cablaggio classi per le dipendenze, che sarebbe stato doloroso tornare indietro.

Per esempio, manca una dipendenza con il Castello:

"Impossibile creare il componente 'MyClass' come ha dipendenze di essere soddisfatto servizio è attesa per i seguenti dipendenze.:

Servizi: - IMyService che non è stato registrato."

Manca una dipendenza senza Castello:

riferimento oggetto non sia impostato su un un'istanza di un oggetto

Morto Ultimo: la possibilità di scambiare servizi iniettato in fase di esecuzione, da parte modifica di un file Xml. La mia percezione è che questa è la caratteristica più tesa, ma la vedo come una semplice ciliegina sulla torta. Preferirei collegare tutti i miei servizi in codice, ma sono sicuro che mi imbatterò in un mal di testa in futuro, dove la mia mente sarà cambiata su questo

Ammetto che - essendo un principiante di IOC e Castle - probabilmente sto solo grattando la superficie, ma finora mi piace davvero quello che vedo. Mi sento come se gli ultimi progetti che ho costruito con esso fossero realmente in grado di reagire ai cambiamenti imprevedibili che sorgono giorno dopo giorno nella mia azienda, un sentimento che non avevo mai avuto prima.

1

Jeremy Frey manca uno dei più grandi motivi per l'utilizzo di un contenitore IOC: rende più facile il codice per deridere e testare.

Incoraggiare l'uso delle interfacce offre molti altri vantaggi: migliore stratificazione, più facile generare dinamicamente proxy per operazioni dichiarative, programmazione orientata agli aspetti e servizi remoti.

Se si pensa che l'IOC sia adatto solo per sostituire le chiamate a "nuovo", non si ottiene.

+0

Non ho menzionato di proposito il test, perché ha menzionato che sta già utilizzando DI. È tutto ciò di cui hai veramente bisogno per prendere in giro e testare. DI è una strategia; I contenitori IoC facilitano questa strategia e rendono più facile l'implementazione (una riduzione del suo "codice macaroni") –

+1

Questo probabilmente avrebbe dovuto essere fatto come commento sul post di Jeremy ... –

13

Inversion of Control si occupa principalmente della gestione delle dipendenze e della fornitura di codice verificabile. Da un approccio classico, se una classe ha una dipendenza, la tendenza naturale è quella di dare alla classe che ha il controllo diretto della dipendenza sulla gestione delle sue dipendenze. Questo di solito significa che la classe che ha la dipendenza "nuove" le sue dipendenze all'interno di un costruttore o su richiesta nei suoi metodi.

Inversion of Control è solo che ... inverte ciò che crea dipendenze, esternalizzando quel processo e iniettandoli nella classe che ha la dipendenza. Di solito, l'entità che crea le dipendenze è ciò che chiamiamo un contenitore IoC, che è responsabile non solo della creazione e dell'iniezione delle dipendenze, ma anche della gestione delle loro vite, della determinazione del loro stile di vita (più su questo in un secondo) e anche di una varietà di altre capacità. (Questo è basato su Castle MicroKernel/Windsor, che è il mio contenitore IoC preferito ... è solidamente scritto, molto funzionale ed estensibile. Esistono altri contenitori IoC che sono più semplici se hai esigenze più semplici, come Ninject, Microsoft Unity e Spring.NET.)

Si consideri che si dispone di un'applicazione interna che può essere utilizzata in un contesto locale o in un contesto remoto. A seconda di alcuni fattori rilevabili, la tua applicazione potrebbe dover caricare implementazioni "locali" dei tuoi servizi e in altri casi potrebbe dover caricare implementazioni "remote" dei tuoi servizi. Se segui l'approccio classico e crei le tue dipendenze direttamente all'interno della classe che ha queste dipendenze, allora quella classe sarà costretta a violare due regole molto importanti sullo sviluppo del software: Separazione dei dubbi e Responsabilità singola. Incroci i limiti di preoccupazione perché la tua classe ora si preoccupa sia del suo scopo intrinseco, sia della preoccupazione di determinare quali dipendenze debba creare e come. La classe è anche responsabile di molte cose, piuttosto che di una singola cosa, e ha molte ragioni per cambiare: il suo scopo intrinseco cambia, il processo di creazione delle sue dipendenze cambia, il modo in cui trova le dipendenze remote, quali dipendenze potrebbero aver bisogno delle loro dipendenze , eccetera.

Invertendo la gestione delle dipendenze, è possibile migliorare l'architettura del sistema e mantenere SoC e SR (o, eventualmente, ottenerlo quando non si era in grado di farlo a causa delle dipendenze.) Poiché un'entità esterna, il contenitore IoC, ora controlla come vengono create e iniettate le tue dipendenze, puoi anche ottenere ulteriori funzionalità. Il contenitore può gestire i cicli di vita delle dipendenze, creando e distruggendoli in modi più flessibili che possono migliorare l'efficienza. Ottieni anche la capacità di gestire gli stili di vita dei tuoi oggetti. Se si dispone di un tipo di dipendenza che viene creato, utilizzato e restituito su una base molto frequente, ma che hanno uno stato piccolo o inesistente (ad esempio, fabbriche), è possibile fornire uno stile di vita in comune, che consentirà al contenitore di creare automaticamente un pool di oggetti per quel particolare tipo di dipendenza. Esistono molti stili di vita e un contenitore come Castle Windsor solitamente ti darà la possibilità di crearne uno tuo.

I contenitori di IoC migliori, come Castle Windsor, offrono anche molta estensibilità. Per impostazione predefinita, Windsor ti consente di creare istanze di tipi locali. È possibile creare strutture che estendono le capacità di creazione del tipo di Windsor per creare dinamicamente proxy di servizi Web e host di servizi WCF in tempo reale, eliminando la necessità di crearli manualmente o staticamente con strumenti come svcutil (questo è qualcosa che ho fatto io stesso di recente .) Esistono molte strutture per portare il supporto IoC di framework esistenti, come NHibernate, ActiveRecord, ecc.

Infine, IoC applica uno stile di codifica che garantisce il codice testabile dell'unità. Uno dei fattori chiave per rendere testabile l'unità di codice è esternalizzare la gestione delle dipendenze. Senza la capacità di fornire dipendenze alternative (fittate, soppresse, ecc.), Testare una singola "unità" di codice in isolamento è un compito molto difficile, lasciando l'integrazione testare l'unico stile alternativo di test automatizzati. Poiché IoC richiede che le classi accettino le dipendenze tramite iniezione (tramite costruttore, proprietà o metodo), ogni classe è solitamente, se non sempre, ridotta a una singola responsabilità di preoccupazione correttamente separata e dipendenze completamente discutibili.

IoC = migliore architettura, maggiore coesione, migliore separazione delle preoccupazioni, classi che sono più facili da ridurre a una singola responsabilità, dipendenze facilmente configurabili e intercambiabili (spesso senza richiedere una ricompilazione del codice), stili di vita di dipendenza flessibile e vita gestione del tempo e codice testabile dell'unità. IoC è una specie di stile di vita ... una filosofia, un approccio per risolvere problemi comuni e per soddisfare le migliori pratiche critiche come SoC e SR.

Anche (o meglio, in particolare) con centinaia di diverse implementazioni di una singola interfaccia, IoC ha molto da offrire. Potrebbe volerci un po 'per avere la testa completamente avvolta attorno ad esso, ma una volta che capisci completamente cos'è IoC e cosa può fare per te, non vorrai mai fare le cose in altro modo (tranne forse lo sviluppo di sistemi embedded ...)

+2

Risposta molto bella. – Klinger

+0

Grazie per questa spiegazione !!! – John

0

I contenitori IoC di solito fanno le iniezioni di dipendenza che in alcuni progetti non sono un grosso problema, ma alcuni dei framework che forniscono contenitori IoC offrono altri servizi che valgono la pena utilizzarli. Castle ad esempio ha un elenco completo di servizi oltre a un contenitore IoC. I proxy dinamici, la gestione delle transazioni e le strutture di NHibernate sono alcuni di essi. Quindi penso che dovresti considerare i contaminatori IoC come parte di un framework di applicazione.

Ecco perché io uso un contenitore CIO: test di unità 1.Writing sarà più facile .Actually si scrive diverse configurazioni da fare diverse cose 2.Adding plugin diversi per i diversi scenari (per i diversi clienti, per esempio) 3. Intercettare le lezioni per aggiungere diversi aspetti al nostro codice. 4. Poiché stiamo utilizzando NHibernate, la gestione delle transazioni e i servizi di NHibernate di Castle sono molto utili per lo sviluppo e la manutenzione del nostro codice. È come se tutti gli aspetti tecnici della nostra applicazione venissero gestiti utilizzando un framework applicativo e abbiamo il tempo di pensare a ciò che i clienti desiderano davvero.

Problemi correlati