Inversion of Control ist ein generisches Entwurfsprinzip der Softwarearchitektur, mit dessen Hilfe wiederverwendbare, modulare Software-Frameworks erstellt werden können, die einfach zu warten sind.
Es ist ein Entwurfsprinzip, bei dem der Kontrollfluss von der generisch geschriebenen Bibliothek oder dem wiederverwendbaren Code "empfangen" wird.
Um es besser zu verstehen, sehen wir uns an, wie wir in unseren früheren Codierungstagen codiert haben. In prozeduralen / traditionellen Sprachen steuert die Geschäftslogik im Allgemeinen den Fluss der Anwendung und "ruft" den generischen oder wiederverwendbaren Code / die Funktionen auf. In einer einfachen Konsolenanwendung wird mein Steuerungsfluss beispielsweise durch die Anweisungen meines Programms gesteuert, die möglicherweise die Aufrufe einiger allgemein wiederverwendbarer Funktionen umfassen.
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
Im Gegensatz dazu sind bei IoC die Frameworks der wiederverwendbare Code, der die Geschäftslogik "aufruft".
In einem Windows-basierten System ist beispielsweise bereits ein Framework zum Erstellen von UI-Elementen wie Schaltflächen, Menüs, Fenstern und Dialogfeldern verfügbar. Wenn ich die Geschäftslogik meiner Anwendung schreibe, werden die Ereignisse des Frameworks meinen Geschäftslogikcode aufrufen (wenn ein Ereignis ausgelöst wird) und NICHT das Gegenteil.
Obwohl der Code des Frameworks meine Geschäftslogik nicht kennt, kann er meinen Code trotzdem aufrufen. Dies wird durch Ereignisse / Delegaten, Rückrufe usw. erreicht. Hier ist die Steuerung des Flusses "invertiert".
Anstatt den Kontrollfluss von statisch gebundenen Objekten abhängig zu machen, hängt der Fluss vom Gesamtobjektgraphen und den Beziehungen zwischen verschiedenen Objekten ab.
Die Abhängigkeitsinjektion ist ein Entwurfsmuster, das das IoC-Prinzip zum Auflösen von Abhängigkeiten von Objekten implementiert.
In einfacheren Worten, wenn Sie versuchen, Code zu schreiben, erstellen und verwenden Sie verschiedene Klassen. Eine Klasse (Klasse A) kann andere Klassen (Klasse B und / oder D) verwenden. Klasse B und D sind also Abhängigkeiten der Klasse A.
Eine einfache Analogie wird ein Klassenauto sein. Ein Auto kann von anderen Klassen wie Motor, Reifen und mehr abhängen.
Die Abhängigkeitsinjektion schlägt vor, dass anstelle der abhängigen Klassen (hier Class Car), die ihre Abhängigkeiten erstellen (Class Engine und Klasse Tire), der Klasse die konkrete Instanz der Abhängigkeit injiziert werden sollte.
Lassen Sie uns mit einem praktischeren Beispiel verstehen. Bedenken Sie, dass Sie Ihren eigenen TextEditor schreiben. Unter anderem können Sie eine Rechtschreibprüfung durchführen, mit der der Benutzer die Tippfehler in seinem Text überprüfen kann. Eine einfache Implementierung eines solchen Codes kann sein:
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;
}
}
Auf den ersten Blick sieht alles rosig aus. Der Benutzer wird Text schreiben. Der Entwickler erfasst den Text, ruft die CheckSpellings-Funktion auf und findet eine Liste der Tippfehler, die er dem Benutzer anzeigt.
Alles scheint gut zu funktionieren, bis ein Benutzer eines schönen Tages anfängt, Französisch im Editor zu schreiben.
Um mehr Sprachen unterstützen zu können, benötigen wir mehr SpellChecker. Wahrscheinlich Französisch, Deutsch, Spanisch usw.
Hier haben wir einen eng gekoppelten Code erstellt, wobei "English" SpellChecker eng mit unserer TextEditor-Klasse gekoppelt ist. Dies bedeutet, dass unsere TextEditor-Klasse vom EnglishSpellChecker abhängig ist oder mit anderen Worten, EnglishSpellCheker ist die Abhängigkeit für TextEditor. Wir müssen diese Abhängigkeit entfernen. Darüber hinaus benötigt unser Texteditor eine Möglichkeit, die konkrete Referenz einer Rechtschreibprüfung nach Ermessen des Entwicklers zur Laufzeit zu speichern.
Wie wir in der Einführung von DI gesehen haben, schlägt es vor, dass die Klasse mit ihren Abhängigkeiten injiziert werden sollte. Es sollte also in der Verantwortung des aufrufenden Codes liegen, alle Abhängigkeiten in die aufgerufene Klasse / den aufgerufenen Code einzufügen. So können wir unseren Code als umstrukturieren
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.
}
}
In unserem Beispiel sollte die TextEditor-Klasse die konkrete Instanz des ISpellChecker-Typs erhalten.
Jetzt kann die Abhängigkeit in einen Konstruktor, eine öffentliche Eigenschaft oder eine Methode eingefügt werden.
Versuchen wir, unsere Klasse mit Constructor DI zu ändern. Die geänderte TextEditor-Klasse sieht ungefähr so aus:
Class TextEditor
{
ISpellChecker objSpellChecker;
string Text;
public void TextEditor(ISpellChecker objSC)
{
objSpellChecker = objSC;
}
public ArrayList <typos> CheckSpellings()
{
return objSpellChecker.CheckSpelling();
}
}
Damit der aufrufende Code beim Erstellen des Texteditors den entsprechenden SpellChecker-Typ in die Instanz des TextEditors einfügen kann.
Den vollständigen Artikel können Sie hier lesen