L'inversione del controllo è un principio di progettazione generico dell'architettura software che aiuta nella creazione di framework software riutilizzabili e modulari di facile manutenzione.
È un principio di progettazione in cui il flusso di controllo viene "ricevuto" dalla libreria scritta generica o dal codice riutilizzabile.
Per capirlo meglio, vediamo come eravamo abituati a programmare nei nostri primi giorni di codifica. Nei linguaggi procedurali / tradizionali, la logica aziendale controlla generalmente il flusso dell'applicazione e "richiama" il codice / le funzioni generici o riutilizzabili. Ad esempio, in una semplice applicazione Console, il mio flusso di controllo è controllato dalle istruzioni del mio programma, che possono includere le chiamate ad alcune funzioni generali riutilizzabili.
print ("Please enter your name:");
scan (&name);
print ("Please enter your DOB:");
scan (&dob);
//More print and scan statements
<Do Something Interesting>
//Call a Library function to find the age (common code)
print Age
In Contrast, con IoC, i Frameworks sono il codice riutilizzabile che "chiama" la logica aziendale.
Ad esempio, in un sistema basato su Windows, sarà già disponibile un framework per creare elementi dell'interfaccia utente come pulsanti, menu, finestre e finestre di dialogo. Quando scrivo la logica di business della mia applicazione, saranno gli eventi del framework a chiamare il mio codice di logica di business (quando viene generato un evento) e NON il contrario.
Sebbene il codice del framework non sia a conoscenza della mia logica aziendale, saprà comunque come chiamare il mio codice. Ciò si ottiene utilizzando eventi / delegati, callback ecc. Qui il controllo del flusso è "invertito".
Quindi, invece di dipendere dal flusso di controllo su oggetti vincolati staticamente, il flusso dipende dall'oggetto grafico complessivo e dalle relazioni tra oggetti diversi.
Dependency Injection è un modello di progettazione che implementa il principio IoC per risolvere le dipendenze degli oggetti.
In parole più semplici, quando provi a scrivere codice, creerai e utilizzerai classi diverse. Una classe (Classe A) può utilizzare altre classi (Classe B e / o D). Quindi, le classi B e D sono dipendenze della classe A.
Una semplice analogia sarà un'auto di classe. Un'auto potrebbe dipendere da altre classi come motore, pneumatici e altro.
Dependency Injection suggerisce che invece delle classi dipendenti (Class Car qui) che creano le sue dipendenze (Class Engine e class Tyre), la classe dovrebbe essere iniettata con l'istanza concreta della dipendenza.
Comprendiamo con un esempio più pratico. Considera che stai scrivendo il tuo TextEditor. Tra le altre cose, puoi avere un correttore ortografico che fornisce all'utente la possibilità di controllare gli errori di battitura nel suo testo. Una semplice implementazione di tale codice può essere:
Class TextEditor
{
//Lot of rocket science to create the Editor goes here
EnglishSpellChecker objSpellCheck;
String text;
public void TextEditor()
{
objSpellCheck = new EnglishSpellChecker();
}
public ArrayList <typos> CheckSpellings()
{
//return Typos;
}
}
A prima vista, tutto sembra roseo. L'utente scriverà del testo. Lo sviluppatore acquisirà il testo e chiamerà la funzione CheckSpellings e troverà un elenco di errori di battitura che mostrerà all'utente.
Tutto sembra funzionare alla grande fino a quando un bel giorno quando un utente inizia a scrivere in francese nell'editor.
Per fornire il supporto per più lingue, abbiamo bisogno di più correttori ortografici. Probabilmente francese, tedesco, spagnolo ecc.
Qui, abbiamo creato un codice strettamente accoppiato con il correttore ortografico "inglese" strettamente associato alla nostra classe TextEditor, il che significa che la nostra classe TextEditor dipende da EnglishSpellChecker o in altre parole EnglishSpellCheker è la dipendenza per TextEditor. Dobbiamo rimuovere questa dipendenza. Inoltre, il nostro editor di testo ha bisogno di un modo per mantenere il riferimento concreto di qualsiasi correttore ortografico basato sulla discrezione dello sviluppatore in fase di esecuzione.
Quindi, come abbiamo visto nell'introduzione di DI, suggerisce che la classe dovrebbe essere iniettata con le sue dipendenze. Quindi, dovrebbe essere responsabilità del codice chiamante iniettare tutte le dipendenze nella classe / codice chiamato. Quindi possiamo ristrutturare il nostro codice come
interface ISpellChecker
{
Arraylist<typos> CheckSpelling(string Text);
}
Class EnglishSpellChecker : ISpellChecker
{
public override Arraylist<typos> CheckSpelling(string Text)
{
//All Magic goes here.
}
}
Class FrenchSpellChecker : ISpellChecker
{
public override Arraylist<typos> CheckSpelling(string Text)
{
//All Magic goes here.
}
}
Nel nostro esempio, la classe TextEditor dovrebbe ricevere l'istanza concreta del tipo ISpellChecker.
Ora, la dipendenza può essere iniettata in Constructor, una proprietà pubblica o un metodo.
Proviamo a cambiare la nostra classe usando Constructor DI. La classe TextEditor modificata avrà un aspetto simile a:
Class TextEditor
{
ISpellChecker objSpellChecker;
string Text;
public void TextEditor(ISpellChecker objSC)
{
objSpellChecker = objSC;
}
public ArrayList <typos> CheckSpellings()
{
return objSpellChecker.CheckSpelling();
}
}
In modo che il codice chiamante, durante la creazione dell'editor di testo, possa iniettare il tipo di spellChecker appropriato nell'istanza di TextEditor.
Puoi leggere l'articolo completo qui