Inversion of Control è un principio di progettazione generico dell'architettura software che aiuta a creare framework software riutilizzabili e modulari che sono facili da mantenere.
È un principio di progettazione in cui il flusso di controllo viene "ricevuto" dalla libreria o dal codice riutilizzabile con scrittura generica.
Per capirlo meglio, vediamo come eravamo abituati a programmare nei nostri primi giorni di programmazione. Nei linguaggi procedurali / tradizionali, la logica aziendale generalmente controlla il flusso dell'applicazione e "Chiama" il codice / le funzioni generiche 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
Al contrario, con IoC, i frame 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 aziendale della mia applicazione, sarebbero gli eventi del framework che chiameranno il mio codice di logica aziendale (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".
Pertanto, anziché dipendere dal flusso di controllo sugli oggetti associati staticamente, il flusso dipende dal grafico generale dell'oggetto e dalle relazioni tra oggetti diversi.
Iniezione delle dipendenze è un modello di progettazione che implementa il principio IoC per risolvere le dipendenze degli oggetti.
In parole più semplici, quando si tenta di scrivere codice, si creano e si usano classi diverse. Una classe (Classe A) può utilizzare altre classi (Classe B e / o D). Pertanto, 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.
Injection Dependency suggerisce che invece delle classi Dependent (Class Car qui) che creano le sue dipendenze (Class Engine e class Tire), 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 controllo ortografico che fornisce all'utente una funzione per 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 Typos che mostrerà all'utente.
Tutto sembra funzionare alla grande fino a un bel giorno in cui un utente inizia a scrivere il francese nell'editor.
Per fornire il supporto per più lingue, dobbiamo disporre di più SpellChecker. Probabilmente francese, tedesco, spagnolo ecc.
Qui, abbiamo creato un codice strettamente accoppiato con SpellChecker "inglese" strettamente accoppiato con la 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 conservare 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 Costruttore, una proprietà pubblica o un metodo.
Proviamo a cambiare la nostra classe usando il costruttore DI. La classe TextEditor modificata sarà 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