Warum heißt Inversion of Control so?

98

Die Wörter invertoder controlwerden überhaupt nicht verwendet, um Inversion of Control in den Definitionen zu definieren, die ich gesehen habe.

Definitionen

Wikipedia

Inversion of Control (IoC) ist eine Programmiertechnik, die hier als objektorientierte Programmierung ausgedrückt wird. Dabei wird die Objektkopplung zur Laufzeit durch ein Assembler-Objekt gebunden und ist zur Kompilierungszeit unter Verwendung der statischen Analyse in der Regel nicht bekannt. ~ http://en.wikipedia.org/wiki/Inversion_of_control

Martin Fowler

Inversion of Control ist ein in der Java-Community weit verbreitetes Muster, mit dem einfache Container verkabelt oder Komponenten aus verschiedenen Projekten zu einer zusammenhängenden Anwendung zusammengefügt werden können. ~ basiert auf http://www.martinfowler.com/articles/injection.html (umformuliert)


Warum heißt Inversion of Control Inversion of Control? Welche Steuerung wird invertiert und von wem? Gibt es eine Möglichkeit, die Umkehrung der Kontrolle mit der Terminologie zu definieren: invertieren und kontrollieren ?

Korey Hinton
quelle
22
Requisiten für die Person, die dies im Klartext
Robert Harvey
2
Wenn eine Definition das Wort verwendet, das sie definiert, wäre dies ein Fehler in einem Wörterbuch. Eine ähnliche Logik gilt hier.
Izkata
2
Siehe auch StackOverflow: Was ist Inversion of Control?
Izkata
2
@Izkata, Wörterbücher definieren Phrasen häufig in Form von eigenen Begriffen oder synonymen Begriffen. dh: die Kraft der Natur Definition verwendet die Worte Kraft und Natur in ihrer Definition oder die Nutzungsbedingungen Definition verwendet die Worte Regeln und Service , wo Regeln mit Begriffen synonym ist.
Korey Hinton
3
Ich bevorzuge aus diesem Grund den Begriff "automatisierte Abhängigkeitsinjektion" oder ADI anstelle von IoC. Fowler selbst erwähnt, dass die meisten aktuellen Paradigmen in der Programmierung eine Art "Umkehrung der Kontrolle" aufweisen, wobei dieser Begriff im Allgemeinen als "die Überlassung der Bestimmungen des auszuführenden Codes und die Zeit, zu der er ausgeführt werden sollte, an eine externe Person definiert wird Kontrollsystem, wenn solche Feststellungen traditionell vom Programm selbst getroffen werden ". Alles von Hardware-Interrupts über ereignisgesteuerte Programmierung bis hin zu virtuellen Maschinen und Multitasking geschieht mit IoC.
KeithS

Antworten:

67

Angenommen, Sie haben eine Art "Repository" -Klasse, und dieses Repository ist dafür verantwortlich, Ihnen Daten aus einer Datenquelle zu übergeben.

Das Repository kann selbst eine Verbindung zur Datenquelle herstellen. Was aber, wenn Sie damit eine Verbindung zur Datenquelle über den Konstruktor des Repositorys übergeben können?

Indem Sie dem Aufrufer erlauben, die Verbindung bereitzustellen, haben Sie die Abhängigkeit der Datenquellenverbindung von der Repository-Klasse entkoppelt, sodass jede Datenquelle mit dem Repository arbeiten kann, nicht nur mit dem vom Repository angegebenen.

Sie haben die Kontrolle umgekehrt, indem Sie die Verantwortung für das Herstellen der Verbindung von der Repository-Klasse an den Aufrufer übergeben haben.

Martin Fowler schlägt vor, den Begriff "Abhängigkeitsinjektion" zu verwenden, um diese Art der Inversion der Steuerung zu beschreiben, da die Inversion der Steuerung als Konzept breiter angewendet werden kann, als nur Abhängigkeiten in eine Konstruktormethode zu injizieren.

Robert Harvey
quelle
3
Dies ist ein Beispiel für die Abhängigkeitsinjektion, aber die Abhängigkeitsinjektion ist nur eine Teilmenge der Inversion der Steuerung. Es gibt andere Dinge, die absolut nichts mit der Abhängigkeitsinjektion zu tun haben und die eine Umkehrung der Kontrolle praktizieren.
dsw88
1
@ mustang2009cobra: Danke. Ein Beispiel ist das, wonach ich gesucht habe, kein umfassender Katalog von Fällen, in denen es vorkommt, und der Begriff "Inversion of Control" ist am engsten mit DI verbunden, nicht mit Message Passing oder anderen ähnlichen Konzepten.
Robert Harvey
Richtig, Abhängigkeitsinjektion ist das am weitesten verbreitete Beispiel für IoC, daher ist ein Beispiel für DI am sinnvollsten. Vielleicht eine kleine Änderung Ihrer Antwort, die angibt, dass dies ein DI-Beispiel ist, welches der bekannteste Typ von IoC ist?
DSW88
1
Genau das habe ich gesucht! Viele Beispiele und Definitionen von IoC spezifizieren nicht eindeutig, welche Steuerung invertiert wird. Mir ist klar, dass IoC mehr kann als nur Abhängigkeitsinjektion, aber jetzt hat endlich etwas in meinem Gehirn geklickt. Vielen Dank!
Korey Hinton
79

Ich denke, niemand kann es besser erklären als Martin Fowler, weiter unten in dem Artikel, mit dem Sie verlinkt sind .

Bei dieser neuen Art von Containern geht es bei der Inversion darum, wie sie nach einer Plugin-Implementierung suchen. In meinem naiven Beispiel hat der Lister die Finder-Implementierung gesucht, indem er sie direkt instanziiert hat. Dies verhindert, dass der Finder ein Plugin ist. Der Ansatz, den diese Container verwenden, besteht darin, sicherzustellen, dass jeder Benutzer eines Plugins einer Konvention folgt, die es einem separaten Assembler-Modul ermöglicht, die Implementierung in den Lister zu injizieren.

Wie er in den obigen Abschnitten erklärt, ist dies nicht ganz derselbe Grund, aus dem der Begriff "Inversion of Control" entstand.

Wenn diese Container darüber sprechen, wie nützlich sie sind, weil sie "Inversion of Control" implementieren, bin ich sehr verwirrt. Die Umkehrung der Kontrolle ist ein gemeinsames Merkmal von Frameworks. Wenn man also sagt, dass diese leichten Container etwas Besonderes sind, weil sie eine Umkehrung der Kontrolle verwenden, dann ist das so, als ob mein Auto etwas Besonderes ist, weil es Räder hat.

Die Frage ist, welchen Aspekt der Kontrolle invertieren sie? Als ich zum ersten Mal auf die Umkehrung der Steuerung stieß, befand sie sich in der Hauptsteuerung einer Benutzeroberfläche. Frühe Benutzeroberflächen wurden vom Anwendungsprogramm gesteuert. Sie hätten eine Folge von Befehlen wie "Name eingeben", "Adresse eingeben"; Ihr Programm würde die Eingabeaufforderungen auslösen und eine Antwort auf jede einzelne aufgreifen. Bei grafischen (oder sogar bildschirmbasierten) Benutzeroberflächen würde das UI-Framework diese Hauptschleife enthalten und Ihr Programm stattdessen Ereignishandler für die verschiedenen Felder auf dem Bildschirm bereitstellen. Die Hauptsteuerung des Programms wurde invertiert, von Ihnen weg in das Framework verschoben.

Aus diesem Grund prägt er den Begriff "Abhängigkeitsinjektion", um diese spezifische Implementierung von Inversion of Control zu behandeln.

Aus diesem Grund denke ich, dass wir einen genaueren Namen für dieses Muster benötigen. Die Umkehrung der Kontrolle ist ein zu allgemeiner Begriff und wird daher von den Menschen als verwirrend empfunden. Infolge zahlreicher Diskussionen mit verschiedenen IoC-Befürwortern haben wir uns für den Namen Dependency Injection entschieden.

Um es ein wenig zu verdeutlichen: Umkehrung der Kontrolle bedeutet alles, was die Kontrollstruktur eines Programms von der klassischen prozeduralen Gestaltung umkehrt.

In früheren Zeiten war ein Schlüsselbeispiel dafür, dass ein Framework die Kommunikation zwischen einer Benutzeroberfläche und Ihrem Code handhabte, anstatt Ihren Code für die direkte Generierung der Benutzeroberfläche zu belassen.

In jüngerer Zeit (als solche Frameworks so ziemlich dominierten und die Frage nicht mehr relevant war) war ein Beispiel die Umkehrung der Kontrolle über die Instanziierung von Objekten.

Fowler und andere entschieden, dass der Begriff Inversion of Control zu viele Techniken abdeckte, und wir brauchten einen neuen Begriff für das spezifische Beispiel der Instanziierung von Objekten (Abhängigkeitsinjektion), aber zu dem Zeitpunkt, als diese Vereinbarung getroffen worden war, den Ausdruck "IoC-Container" "war ausgezogen.

Dies trübt das Wasser sehr, da ein IoC-Container eine bestimmte Art von Abhängigkeitsinjektion ist, aber Abhängigkeitsinjektion ist eine bestimmte Art von Inversion der Steuerung. Dies ist der Grund, warum Sie so verwirrte Antworten erhalten, egal wo Sie hinschauen.

pdr
quelle
4
Normalerweise stimme ich nicht so oft ab, aber das ist eine großartige Antwort. 1 Stimme wird nicht ausreichen. :(
RJD22
1
Diese Antwort beantwortet wirklich alle Fragen der IoC- und DI-Verwirrungen.
Ali Umair
32

Hier sind die "regulären" Kontrollflussprogramme, die normalerweise befolgt werden:

  • Befehle nacheinander ausführen
  • Sie behalten die Kontrolle über den Kontrollfluss des Programms

Inversion of Control "kehrt" diesen Kontrollfluss um, was bedeutet, dass er auf den Kopf gestellt wird:

  • Ihr Programm kontrolliert den Fluss nicht mehr. Anstatt Befehle nach Belieben aufzurufen, warten Sie darauf , dass Sie jemand anderes anruft .

Diese letzte Zeile ist wichtig. Anstatt jemanden anzurufen, wenn Sie Lust dazu haben, ruft Sie jemand anders an, wenn er Lust dazu hat.

Ein häufiges Beispiel hierfür sind Web-Frameworks wie Rails. Sie definieren Controller, aber Sie entscheiden nicht, wann diese aufgerufen werden. Rails ruft sie an, wenn Bedarf besteht.

dsw88
quelle
19
[obligatorischen "in sowjetischen" Witz hier einfügen]
Robert Harvey
2
Scherz beiseite, ich denke, Sie beschreiben eher das Weitergeben von Nachrichten, das seit Jahrzehnten ein Grundnahrungsmittel von OO ist.
Robert Harvey
3
@ JimmyHoffa - Das ist überhaupt nicht falsch - siehe hier . Die Umkehrung der Kontrolle ist ein allgemeineres Konzept als das Erzeugen von Abhängigkeiten, und die Abhängigkeitsinjektion ist nur eine Form der IoC.
Lee
7
@ JimmyHoffa: Ich bin anderer Meinung. Dies ist ebenso eine korrekte Definition von Inversion of Control wie Ihre Antwort oder Roberts, was genau die Verwirrung ist, die Fowler beschreibt, wenn Sie die Phrase Dependency Injection (die Sie beide als IoC bezeichnen) prägen.
pdr
5
Ich stimme dieser Aussage von @pdr zu: "Inversion of Control bedeutet alles, was die Kontrollstruktur eines Programms vom klassischen prozeduralen Design abhebt." Das ist, was ich beschreibe, und das ist eine generische Umkehrung der Kontrolle. Dependency Injection ist eine Art von IoC, aber es ist nicht das einzige, was IoC praktiziert
dsw88
13

Es geht darum, wer die Instanziierung von Abhängigkeiten steuert.

Wenn eine Klasse / Methode eine andere Klasse (Abhängigkeit) verwenden muss, wird sie traditionell direkt von der Klasse / Methode instanziiert. Es steuert seine Abhängigkeiten.

Mit Inversion of Control (IoC) hat der Aufrufer die Abhängigkeit übergeben, wodurch die Abhängigkeit instanziiert wird. Der Aufrufer steuert die Abhängigkeiten.

Die Kontrolle darüber, wo eine Abhängigkeit instanziiert wird, wurde invertiert. Anstatt "unten" zu sein, wo der Code ist, der sie benötigt, wird sie "oben" instanziiert, wo der Code, der sie benötigt, aufgerufen wird.

Oded
quelle
1
Nicht einfach Englisch. Scheitern.
Robert Harvey
2
@RobertHarvey - wie schlicht ist schlicht?
Oded
4
@RobertHarvey Huh? Das scheint mir ziemlich klar zu sein ... Was ist nicht klar? Das Wort "Instanziierung" oder "Abhängigkeiten"?
Jimmy Hoffa
@ JimmyHoffa: Siehe meine Antwort, die ein konkretes Beispiel liefert. IoC ist einer dieser lästigen Begriffe, die jeder verwendet, aber keiner erklärt; Menschen verwenden IoC-Container in Programmen, die sie nicht benötigen, weil sie sie falsch verstehen. Das heißt, ich denke, diese Antwort hatte ein paar Ninja-Änderungen. :)
Robert Harvey
Dies und der obige Beitrag be @ dsw88 sind für mich ganz klar. Wenn ich noch nie von IoC gehört hätte, würde ich intuitiv denken: "Oh, wer etwas steuern darf, wechselt von einer Seite zur anderen." Gute Arbeit dabei!
Oliver Williams
2

Typischerweise ruft Code höherer Ebene Code niedrigerer Ebene auf (dh steuert diesen). Main () ruft function (), function () libraryFunction () auf.

Das kann invertiert werden, so dass die Bibliotheksfunktion der unteren Ebene Funktionen der höheren Ebene aufruft.

Warum würdest du das tun? Middleware. Manchmal möchten Sie die oberste und die unterste Ebene steuern, aber es gibt eine Menge Arbeit in der Mitte, die Sie einfach nicht tun möchten. Nehmen Sie die Implementierung von QuickSort in die C-Stdlib . Sie rufen QuickSort auf der obersten Ebene an. Sie übergeben qsort () einen Funktionszeiger auf Ihre eigene Funktion, der einen Komparator für jede beliebige Funktion implementiert. Wenn qsort () aufgerufen wird, ruft es diese Komparatorfunktion auf. qsort () steuert / ruft / steuert Ihre High-Level-Funktion.

Philip
quelle
1

Hier ist eine einfache Übersicht:

  1. Die Steuerung bezieht sich darauf, was das Programm als nächstes tut
  2. Auf der obersten Ebene steuern normalerweise zwei Dinge das Steuerelement: die Anwendung selbst und der Benutzer

In früheren Zeiten gehörte die Kontrolle zuerst der Anwendung und dann dem Benutzer. Wenn die Anwendung etwas vom Benutzer benötigte, hielt sie an und fragte und ging dann zur nächsten Aufgabe über. Die Interaktion des Benutzers lieferte hauptsächlich Daten, anstatt zu steuern, was die Anwendung als Nächstes tat. Das ist uns heutzutage ein bisschen fremd, da wir diese Art von Verhalten nicht sehr oft sehen.

Wenn wir das umschalten und dem Benutzer die primäre Kontrolle geben, dann haben wir die Kontrolle invertiert. Dies bedeutet, dass der Benutzer nicht darauf wartet, dass die Anwendung etwas zu tun gibt, sondern darauf wartet, dass der Benutzer ihm etwas zu tun gibt. GUIs sind ein großartiges Beispiel dafür, und so ziemlich alles, was eine Ereignisschleife enthält, hat die Steuerung umgekehrt.

Beachten Sie, dass sich mein Beispiel auf der obersten Ebene befindet und dass dieses Konzept der Inversion der Steuerung auf verschiedene Steuerungsebenen innerhalb der Anwendung abstrahiert werden kann (dh Abhängigkeitsinjektion). Dies mag der Grund sein, warum es so schwierig ist, eine eindeutige Antwort zu erhalten.

Loren
quelle
0

Versuchen wir es anhand der beiden Beispiele zu verstehen.

Beispiel 1

In früheren Tagen haben Apps zum Generieren von Eingabeaufforderungen verwendet, um Benutzereingaben nacheinander zu akzeptieren. Heutzutage instanziieren UI-Frameworks verschiedene UI-Elemente, durchlaufen verschiedene Ereignisse dieser UI-Elemente (wie Mauszeiger, Mausklick usw.) und Benutzer- / Hauptprogramme stellen Hooks (zum Beispiel UI-Ereignis-Listener in Java) zum Abhören dieser Ereignisse bereit. Daher wird die "Steuerung" des Haupt-UI-Elementflusses vom Benutzerprogramm in das UI-Framework verschoben. In früheren Tagen war es im Anwenderprogramm.

Beispiel 2

Betrachten Sie die folgende Klasse CustomerProcessor:

class CustomerProcessor
{
    SqlCustRepo custRepo = new SqlCustRepo(); 
    private void processCustomers()
    {
            Customers[] custs = custRepo.getAllCusts();
    }
}

Wenn ich processCustomer()von einer Implementierung unabhängig sein möchte getAllCusts(), die nicht nur von der von bereitgestellt wird SqlCustRepo, muss ich die Leitung entfernen: SqlCustRepo custRepo = new SqlCustRepo()und sie durch eine allgemeinere ersetzen, die in der Lage ist, verschiedene Arten der Implementierung zu akzeptieren, sodass die processCustomers()einfach funktioniert eine bereitgestellte Implementierung. Der obige Code (Instanziieren der erforderlichen Klasse SqlCustRepodurch die Hauptprogrammlogik) ist ein traditioneller Weg und erreicht dieses Ziel der Entkopplung processCustomers()von der Implementierung von nicht getAllCusts(). Bei der Inversion der Steuerung instanziiert der Container die erforderliche Implementierungsklasse (wie beispielsweise in der XML-Konfiguration angegeben) und fügt sie in die Hauptprogrammlogik ein, die gemäß den angegebenen Hooks gebunden wird (beispielsweise durch @AutowiredAnnotation oder getBean()Methode im Spring Framework).

Mal sehen, wie das geht. Betrachten Sie den folgenden Code.

Config.xml

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
    <bean id="custRepo" class="JsonCustRepo" />
</beans>

CustRepo.java

interface ICustRepo 
{ ... }

JsonCustRepo.java

class JsonCustRepo implements CustRepo
{ ... }

App.java

class App
{
    public static void main(String[] args) 
    {
        ApplicationContext context = new ClassPathXmlApplicationContext("Config.xml");
        ICustRepo custRepo = (JsonCustRepo) context.getBean("custRepo");
    }
}

Wir können auch haben

class GraphCustRepo implements ICustRepo { ... }   

und

<bean id="custRepo" class="GraphCustRepo">

und wir müssen App.java nicht ändern.

Über dem Container (dem Spring Framework) muss die XML-Datei gescannt, die Bean eines bestimmten Typs instanziiert und in das Benutzerprogramm eingefügt werden. Benutzerprogramme haben keine Kontrolle darüber, welche Klasse instanziiert wird.

PS: IoC ist ein generisches Konzept und wird auf viele Arten erreicht. Die obigen Beispiele erreichen dies durch Abhängigkeitsinjektion.

Referenz: Martin Fowlers Artikel .

Mahesha999
quelle