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 ...)
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") –
Questo probabilmente avrebbe dovuto essere fatto come commento sul post di Jeremy ... –