Was ist Abhängigkeitsinjektion?

3075

Es wurden bereits mehrere Fragen mit spezifischen Fragen zur Abhängigkeitsinjektion gestellt , z. B. wann sie verwendet werden soll und welche Frameworks dafür vorhanden sind. Jedoch,

Was ist Abhängigkeitsinjektion und wann / warum sollte oder sollte sie nicht verwendet werden?

AR.
quelle
Siehe meine Diskussion zur Abhängigkeitsinjektion hier .
Kevin S.
33
Ich stimme den Kommentaren zu Links zu. Ich kann verstehen, dass Sie vielleicht auf jemand anderen verweisen möchten. Aber füge wenigstens hinzu, warum du sie verlinkst und was diesen Link besser macht als die anderen Links, die ich mit Google bekommen könnte
Christian Payne
@AR: Technisch gesehen ist Dependency Injection keine spezielle Form von IoC. IoC ist vielmehr eine Technik, die zur Bereitstellung der Abhängigkeitsinjektion verwendet wird. Andere Techniken könnten verwendet werden, um eine Abhängigkeitsinjektion bereitzustellen (obwohl IoC die einzige häufig verwendete ist), und IoC wird auch für viele andere Probleme verwendet.
Sean Reilly
Eine der schönsten Erklärungen, die ich je über DI gelesen habe, stammt von Google's Guice (ausgesprochen als Saft) http://code.google.com/p/google-guice/wiki/Motivation?tm=6
Raj
136
Denken Sie bei Links daran, dass sie häufig auf die eine oder andere Weise verschwinden. Es gibt eine wachsende Anzahl von toten Links in SO-Antworten. Egal wie gut der verlinkte Artikel ist, es ist überhaupt nicht gut, wenn Sie ihn nicht finden können.
DOK

Antworten:

1931

Die Abhängigkeitsinjektion übergibt die Abhängigkeit an andere Objekte oder Frameworks (Abhängigkeitsinjektor).

Die Abhängigkeitsinjektion erleichtert das Testen. Die Injektion kann durch einen Konstruktor erfolgen .

SomeClass() hat seinen Konstruktor wie folgt:

public SomeClass() {
    myObject = Factory.getObject();
}

Problem : Wenn myObjectkomplexe Aufgaben wie Festplattenzugriff oder Netzwerkzugriff erforderlich sind , ist es schwierig , einen Komponententest durchzuführen SomeClass(). Programmierer müssen sich verspotten myObjectund könnten den Werksaufruf abfangen .

Alternative Lösung :

  • Übergabe myObjectals Argument an den Konstruktor
public SomeClass (MyClass myObject) {
    this.myObject = myObject;
}

myObject kann direkt übergeben werden, was das Testen erleichtert.

  • Eine häufige Alternative ist die Definition eines Do-Nothing-Konstruktors . Die Abhängigkeitsinjektion kann über Setter erfolgen. (h / t @MikeVella).
  • Martin Fowler dokumentiert eine dritte Alternative (h / t @MarcDix), bei der Klassen explizit eine Schnittstelle für die Abhängigkeiten implementieren, die Programmierer einfügen möchten.

Es ist schwieriger, Komponenten in Unit-Tests ohne Abhängigkeitsinjektion zu isolieren.

Als ich 2013 diese Antwort schrieb, war dies ein wichtiges Thema im Google Testing Blog . Dies bleibt für mich der größte Vorteil, da Programmierer nicht immer die zusätzliche Flexibilität in ihrem Laufzeitdesign benötigen (z. B. für Service Locator oder ähnliche Muster). Programmierer müssen die Klassen häufig während des Testens isolieren.

wds
quelle
25
In Anerkennung der Tatsache, dass Ben Hoffsteins Verweis auf Martin Fowlers Artikel notwendig ist, um auf dieses Thema hinzuweisen, akzeptiere ich die Antwort von wds, da sie die Frage hier auf SO tatsächlich beantwortet.
AR.
121
+1 für Erklärung und Motivation: Die Erstellung von Objekten, von denen eine Klasse abhängt, hängt vom Problem eines anderen ab . Eine andere Möglichkeit zu sagen ist, dass DI den Unterricht kohärenter macht (sie haben weniger Verantwortlichkeiten).
Fuhrmanator
13
Sie sagen, die Abhängigkeit wird "an den Konstruktor" übergeben, aber so wie ich es verstehe, ist dies nicht unbedingt richtig. Es ist immer noch eine Abhängigkeitsinjektion, wenn die Abhängigkeit als Eigenschaft festgelegt wird, nachdem das Objekt instanziiert wurde. Richtig?
Mike Vella
1
@ MikeVella Ja, das ist richtig. In den meisten Fällen macht dies keinen wirklichen Unterschied, obwohl die Eigenschaften im Allgemeinen etwas flexibler sind. Ich werde den Text leicht bearbeiten, um darauf hinzuweisen.
WDS
2
Eine der besten Antworten, die ich bisher gefunden habe, daher bin ich wirklich daran interessiert, sie zu verbessern. Es fehlt eine Beschreibung der dritten Form der Abhängigkeitsinjektion: Schnittstelleninjektion .
Marc Dix
2351

Die beste Definition, die ich bisher gefunden habe, ist eine von James Shore :

"Dependency Injection" ist ein 25-Dollar-Begriff für ein 5-Cent-Konzept. [...] Abhängigkeitsinjektion bedeutet, einem Objekt seine Instanzvariablen zu geben. [...].

Es gibt einen Artikel von Martin Fowler , der sich ebenfalls als nützlich erweisen könnte.

Die Abhängigkeitsinjektion stellt im Wesentlichen die Objekte bereit, die ein Objekt benötigt (seine Abhängigkeiten), anstatt sie selbst erstellen zu lassen. Dies ist eine sehr nützliche Technik zum Testen, da damit Abhängigkeiten verspottet oder ausgeblendet werden können.

Abhängigkeiten können auf viele Arten in Objekte injiziert werden (z. B. Konstruktorinjektion oder Setterinjektion). Man kann sogar spezielle Abhängigkeitsinjektions-Frameworks (z. B. Spring) verwenden, um dies zu tun, aber sie sind sicherlich nicht erforderlich. Sie benötigen diese Frameworks nicht, um eine Abhängigkeitsinjektion durchzuführen. Das explizite Instanziieren und Übergeben von Objekten (Abhängigkeiten) ist eine ebenso gute Injektion wie die Injektion durch das Framework.

Thiago Arrais
quelle
35
Ich mag die Erklärung von James 'Artikel, insbesondere das Ende: "Trotzdem muss man sich über jeden Ansatz wundern, der drei Konzepte (' TripPlanner ',' CabAgency 'und' AirlineAgency ') in mehr als neun Klassen umwandelt. und fügt dann Dutzende Zeilen Klebercode und Konfigurations-XML hinzu, bevor eine einzelne Zeile Anwendungslogik geschrieben wird. " Dies ist, was ich sehr oft (leider) gesehen habe - diese Abhängigkeitsinjektion (die per se gut ist, wie von ihm erklärt) wird missbraucht, um Dinge zu komplizieren, die einfacher hätten gemacht werden können - und am Ende "unterstützenden" Code zu schreiben ...
Matt
2
Betreff: "Das explizite Instanziieren und Übergeben von Objekten (Abhängigkeiten) ist eine ebenso gute Injektion wie eine Injektion durch das Framework." Warum haben die Leute Rahmenbedingungen dafür geschaffen?
Dzieciou
13
Aus dem gleichen Grund, aus dem jedes Framework geschrieben wird (oder zumindest geschrieben werden sollte): Weil es viele wiederholte / Boilerplate-Codes gibt, die geschrieben werden müssen, sobald Sie eine bestimmte Komplexität erreicht haben. Das Problem ist, dass die Leute oft nach einem Framework greifen, auch wenn es nicht unbedingt benötigt wird.
Thiago Arrais
14
Die 25-Dollar-Laufzeit für ein 5-Cent-Konzept ist abgelaufen. Hier ist ein guter Artikel, der mir geholfen hat: codeproject.com/Articles/615139/…
Christine
@Matt die Konfigurationsdateien sind nur die "Verschiebung der Entscheidung" bis zum Äußersten - "Verschiebung der Entscheidung bis zur tatsächlichen Laufzeit". Dagger und insbesondere Dagger haben meiner Meinung nach den Sweet Spot "Verschiebung der Entscheidung bis zur Montage der Bewerbung" gefunden.
Thorbjørn Ravn Andersen
645

Ich fand dieses lustige Beispiel in Bezug auf lose Kopplung :

Jede Anwendung besteht aus vielen Objekten, die zusammenarbeiten, um einige nützliche Dinge auszuführen. Traditionell ist jedes Objekt dafür verantwortlich, seine eigenen Verweise auf die abhängigen Objekte (Abhängigkeiten) zu erhalten, mit denen es zusammenarbeitet. Dies führt zu stark gekoppelten Klassen und schwer zu testendem Code.

Betrachten Sie zum Beispiel a Car Objekt.

A Carhängt von Rädern, Motor, Kraftstoff, Batterie usw. ab. Traditionell definieren wir die Marke solcher abhängigen Objekte zusammen mit der Definition des CarObjekts.

Ohne Abhängigkeitsinjektion (DI):

class Car{
  private Wheel wh = new NepaliRubberWheel();
  private Battery bt = new ExcideBattery();

  //The rest
}

Hier ist das CarObjekt für die Erstellung der abhängigen Objekte verantwortlich.

Was ist, wenn wir den Typ seines abhängigen Objekts - sagen wir Wheel- nach den anfänglichen NepaliRubberWheel()Einstichen ändern möchten ? Wir müssen beispielsweise das Car-Objekt mit seiner neuen Abhängigkeit neu erstellen ChineseRubberWheel(), aber das Carkann nur der Hersteller.

Was macht das Dependency Injectiondann für uns ...?

Bei Verwendung der Abhängigkeitsinjektion erhalten Objekte ihre Abhängigkeiten zur Laufzeit und nicht zur Kompilierungszeit (Autoherstellungszeit) . Damit wir jetzt das ändern können, Wheelwann immer wir wollen. Hier kann das dependency( wheel) Carzur Laufzeit injiziert werden .

Nach Verwendung der Abhängigkeitsinjektion:

Hier injizieren wir die Abhängigkeiten (Rad und Batterie) zur Laufzeit. Daher der Begriff: Abhängigkeitsinjektion.

class Car{
  private Wheel wh; // Inject an Instance of Wheel (dependency of car) at runtime
  private Battery bt; // Inject an Instance of Battery (dependency of car) at runtime
  Car(Wheel wh,Battery bt) {
      this.wh = wh;
      this.bt = bt;
  }
  //Or we can have setters
  void setWheel(Wheel wh) {
      this.wh = wh;
  }
}

Quelle: Abhängigkeitsinjektion verstehen

gtiwari333
quelle
20
Ich verstehe dies so, dass wir, anstatt ein neues Objekt als Teil eines anderen Objekts zu instanziieren, dieses Objekt einfügen können, wenn und falls es benötigt wird, wodurch die Abhängigkeit des ersten Objekts davon beseitigt wird. Ist das richtig?
JeliBeanMachine
Ich habe dies mit einem Coffee-Shop-Beispiel hier beschrieben: digigene.com/design-patterns/dependency-injection-coffeeshop
Ali Nem
11
Mag diese Analogie wirklich, weil es einfaches Englisch ist, das eine einfache Analogie verwendet. Angenommen, ich bin Toyota, habe bereits zu viel Geld und Personal für die Herstellung eines Autos vom Design bis zum Abrollen der Montagelinie ausgegeben. Wenn es seriöse Reifenhersteller gibt, warum sollte ich dann bei Null anfangen, um eine Reifenherstellungsabteilung zu gründen, dh neweine Reifen? Ich nicht. Alles was ich tun muss ist bei ihnen zu kaufen (über param injizieren), zu installieren und wah-lah! Zurück zur Programmierung: Wenn ein C # -Projekt eine vorhandene Bibliothek / Klasse verwenden muss, gibt es zwei Möglichkeiten zum Ausführen / Debuggen. 1-Verweis auf das gesamte Projekt hinzufügen
Jeb50
(nicht), .. externe Bibliothek / Klasse oder 2-Add aus der DLL. Wenn wir nicht sehen müssen, was sich in dieser externen Klasse befindet, ist das Hinzufügen als DLL einfacher. Also ist Option 1 dazu new, Option 2 ist es, es als Parameter zu übergeben. Kann nicht genau sein, aber einfach dumm leicht zu verstehen.
Jeb50
1
@JeliBeanMachine (Entschuldigung für die extrem späte Antwort auf einen Kommentar ..) Es ist nicht so, dass wir die Abhängigkeit des ersten Objekts vom Radobjekt oder Batterieobjekt entfernen, sondern dass wir ihm die Abhängigkeit übergeben, damit wir die Instanz oder Implementierung des ändern können Abhängigkeit. Vorher: Auto hat eine fest codierte Abhängigkeit von NepaliRubberWheel. Nachher: ​​Auto hat eine injizierte Abhängigkeit von der Instanz von Wheel.
Mikael Ohlson
263

Die Abhängigkeitsinjektion ist eine Praxis, bei der Objekte so entworfen werden, dass sie Instanzen der Objekte von anderen Codeteilen empfangen, anstatt sie intern zu erstellen. Dies bedeutet, dass jedes Objekt, das die für das Objekt erforderliche Schnittstelle implementiert, ersetzt werden kann, ohne den Code zu ändern, was das Testen vereinfacht und die Entkopplung verbessert.

Betrachten Sie zum Beispiel diese Klassen:

public class PersonService {
  public void addManager( Person employee, Person newManager ) { ... }
  public void removeManager( Person employee, Person oldManager ) { ... }
  public Group getGroupByManager( Person manager ) { ... }
}

public class GroupMembershipService() {
  public void addPersonToGroup( Person person, Group group ) { ... }
  public void removePersonFromGroup( Person person, Group group ) { ... }
} 

In diesem Beispiel würde die Implementierung von PersonService::addManagerund PersonService::removeManagereine Instanz von benötigen GroupMembershipService, um ihre Arbeit zu erledigen. Ohne Abhängigkeitsinjektion besteht die traditionelle Methode darin, ein neues GroupMembershipServiceim Konstruktor von zu instanziieren PersonServiceund dieses Instanzattribut in beiden Funktionen zu verwenden. Wenn der Konstruktor von GroupMembershipServicejedoch mehrere Dinge hat, die er benötigt, oder noch schlimmer, gibt es einige Initialisierungs- "Setter", die aufgerufen werden müssen GroupMembershipService, der Code wächst ziemlich schnell, und das PersonServiceJetzt hängt nicht nur von dem, GroupMembershipServicesondern auch von allem anderen ab GroupMembershipServicekommt drauf an. Darüber hinaus ist die Verknüpfung zu fest GroupMembershipServicein das codiert, PersonServicewas bedeutet, dass Sie a nicht "blenden" könnenGroupMembershipService zu Testzwecken oder zur Verwendung eines Strategiemusters in verschiedenen Teilen Ihrer Anwendung.

Mit Dependency Injection würden Sie das GroupMembershipServicein Ihrem Konstruktor übergeben , anstatt PersonServicees in Ihrem zu instanziieren PersonService, oder eine Eigenschaft (Getter und Setter) hinzufügen, um eine lokale Instanz davon festzulegen. Dies bedeutet, dass Sie sich PersonServicekeine Gedanken mehr darüber machen müssen, wie Sie eine erstellen GroupMembershipService, sondern nur die angegebenen akzeptieren und mit ihnen arbeiten. Dies bedeutet auch, dass alles, was eine Unterklasse von GroupMembershipServiceist oder die GroupMembershipServiceSchnittstelle implementiert, in die Schnittstelle "injiziert" werden PersonServicekann und PersonServicedass die Änderung nicht bekannt sein muss.

Adam Ness
quelle
29
Wäre toll gewesen, wenn Sie das gleiche Codebeispiel NACH der Verwendung von DI
CodyBugstein
170

Die akzeptierte Antwort ist gut - aber ich möchte noch hinzufügen, dass DI dem klassischen Vermeiden von hartcodierten Konstanten im Code sehr ähnlich ist.

Wenn Sie eine Konstante wie einen Datenbanknamen verwenden, verschieben Sie diese schnell aus dem Code in eine Konfigurationsdatei und übergeben eine Variable mit diesem Wert an die Stelle, an der sie benötigt wird. Der Grund dafür ist, dass sich diese Konstanten normalerweise häufiger ändern als der Rest des Codes. Zum Beispiel, wenn Sie den Code in einer Testdatenbank testen möchten.

DI ist in der Welt der objektorientierten Programmierung analog dazu. Die Werte dort anstelle von konstanten Literalen sind ganze Objekte - aber der Grund, den Code, der sie erstellt, aus dem Klassencode zu verschieben, ist ähnlich - die Objekte ändern sich häufiger als der Code, der sie verwendet. Ein wichtiger Fall, in dem eine solche Änderung erforderlich ist, sind Tests.

zby
quelle
18
+1 "Die Objekte ändern sich häufiger als der Code, der sie verwendet". Fügen Sie zur Verallgemeinerung eine Indirektion an Flusspunkten hinzu. Je nach Flusspunkt werden die Indirektionen mit unterschiedlichen Namen bezeichnet !!
Chethan
139

Versuchen wir ein einfaches Beispiel mit Auto- und Motorklassen. Jedes Auto benötigt einen Motor, um irgendwohin zu fahren, zumindest für den Moment. Also unten, wie Code ohne Abhängigkeitsinjektion aussehen wird.

public class Car
{
    public Car()
    {
        GasEngine engine = new GasEngine();
        engine.Start();
    }
}

public class GasEngine
{
    public void Start()
    {
        Console.WriteLine("I use gas as my fuel!");
    }
}

Und um die Autoklasse zu instanziieren, verwenden wir den nächsten Code:

Car car = new Car();

Das Problem mit diesem Code, das wir eng mit GasEngine verbunden haben, und wenn wir uns entscheiden, ihn in ElectricityEngine zu ändern, müssen wir die Fahrzeugklasse neu schreiben. Und je größer die Anwendung, desto mehr Probleme und Kopfschmerzen müssen wir hinzufügen und neue Motortypen verwenden.

Mit anderen Worten, bei diesem Ansatz ist unsere High-Level-Car-Klasse von der untergeordneten GasEngine-Klasse abhängig, die gegen das Dependency Inversion Principle (DIP) von SOLID verstößt. DIP schlägt vor, dass wir uns auf Abstraktionen verlassen sollten, nicht auf konkrete Klassen. Um dies zu gewährleisten, führen wir die IEngine-Schnittstelle ein und schreiben den Code wie folgt neu:

    public interface IEngine
    {
        void Start();
    }

    public class GasEngine : IEngine
    {
        public void Start()
        {
            Console.WriteLine("I use gas as my fuel!");
        }
    }

    public class ElectricityEngine : IEngine
    {
        public void Start()
        {
            Console.WriteLine("I am electrocar");
        }
    }

    public class Car
    {
        private readonly IEngine _engine;
        public Car(IEngine engine)
        {
            _engine = engine;
        }

        public void Run()
        {
            _engine.Start();
        }
    }

Jetzt ist unsere Fahrzeugklasse nur noch von der IEngine-Schnittstelle abhängig, nicht von einer bestimmten Implementierung des Motors. Der einzige Trick besteht nun darin, wie wir eine Instanz des Autos erstellen und ihm eine tatsächliche konkrete Motorklasse wie GasEngine oder ElectricityEngine geben. Hier kommt Dependency Injection ins Spiel.

   Car gasCar = new Car(new GasEngine());
   gasCar.Run();
   Car electroCar = new Car(new ElectricityEngine());
   electroCar.Run();

Hier injizieren (übergeben) wir grundsätzlich unsere Abhängigkeit (Engine-Instanz) an den Car-Konstruktor. Jetzt haben unsere Klassen eine lose Kopplung zwischen Objekten und ihren Abhängigkeiten, und wir können problemlos neue Motortypen hinzufügen, ohne die Fahrzeugklasse zu ändern.

Der Hauptvorteil der Abhängigkeitsinjektion besteht darin, dass Klassen lockerer gekoppelt sind, da sie keine fest codierten Abhängigkeiten aufweisen. Dies folgt dem oben erwähnten Prinzip der Abhängigkeitsinversion. Anstatt auf bestimmte Implementierungen zu verweisen, fordern Klassen Abstraktionen (normalerweise Schnittstellen ) an, die ihnen beim Erstellen der Klasse zur Verfügung gestellt werden.

Letztendlich ist die Abhängigkeitsinjektion also nur eine Technik, um eine lose Kopplung zwischen Objekten und ihren Abhängigkeiten zu erreichen. Anstatt Abhängigkeiten, die die Klasse zur Ausführung ihrer Aktionen benötigt, direkt zu instanziieren, werden der Klasse Abhängigkeiten (meistens) über die Konstruktorinjektion bereitgestellt.

Auch wenn wir viele Abhängigkeiten haben, ist es sehr empfehlenswert, IoC-Container (Inversion of Control) zu verwenden, mit denen wir feststellen können, welche Schnittstellen welchen konkreten Implementierungen für alle unsere Abhängigkeiten zugeordnet werden sollen, und diese Abhängigkeiten beim Erstellen für uns auflösen können unser Objekt. Beispielsweise könnten wir in der Zuordnung für den IoC-Container angeben, dass die IEngine- Abhängigkeit der GasEngine- Klasse zugeordnet werden soll. Wenn wir den IoC-Container nach einer Instanz unserer Car- Klasse fragen , wird unsere Car- Klasse automatisch mit einer GasEngine- Abhängigkeit erstellt übergeben.

UPDATE: Ich habe kürzlich einen Kurs über EF Core von Julie Lerman gesehen und mochte auch ihre kurze Definition über DI.

Die Abhängigkeitsinjektion ist ein Muster, mit dem Ihre Anwendung Objekte im laufenden Betrieb in Klassen injizieren kann, die sie benötigen, ohne diese Klassen zu zwingen, für diese Objekte verantwortlich zu sein. Dadurch kann Ihr Code lockerer gekoppelt werden, und Entity Framework Core wird an dasselbe Dienstsystem angeschlossen.

user2771704
quelle
2
Wie unterscheidet sich das aus Neugier vom Strategiemuster? Dieses Muster kapselt die Algorithmen und macht sie austauschbar. Es fühlt sich so an, als ob Abhängigkeitsinjektion und Strategiemuster sehr ähnlich sind.
Elixier
110

Stellen wir uns vor, Sie möchten angeln gehen:

  • Ohne Abhängigkeitsinjektion müssen Sie sich selbst um alles kümmern. Sie müssen ein Boot finden, eine Angelrute kaufen, nach Ködern suchen usw. Es ist natürlich möglich, aber es bringt viel Verantwortung auf Sie. In Bezug auf die Software bedeutet dies, dass Sie nach all diesen Dingen suchen müssen.

  • Bei der Abhängigkeitsinjektion kümmert sich jemand anderes um die gesamte Vorbereitung und stellt Ihnen die erforderliche Ausrüstung zur Verfügung. Sie erhalten das Boot, die Angelrute und den Köder - alles einsatzbereit.

Olivier Liechti
quelle
59
Die Kehrseite ist, stellen Sie sich vor, Sie beauftragen einen Klempner, um Ihr Badezimmer zu renovieren. Dieser sagt dann: "Großartig, hier ist eine Liste der Werkzeuge und Materialien, die Sie für mich benötigen." Sollte das nicht die Aufgabe des Klempners sein?
Jscs
Damit sich jemand um jemanden kümmern muss, von dem er nichts zu wissen hat, beschließt er dennoch, die Liste der Boote, Stöcke und Köder zusammenzustellen - wenn auch gebrauchsfertig.
Chookoos
22
@ JoshCaswell Nein, das wäre die Aufgabe des Klempnerarbeiters. Als Kunde müssen Sie die Installation durchführen. Dafür brauchen Sie einen Klempner. Der Klempner braucht seine Werkzeuge. Um diese zu bekommen, wird es von der Sanitärfirma ausgestattet. Als Kunde möchten Sie nicht genau wissen, was der Klempner tut oder braucht. Als Klempner wissen Sie, was Sie brauchen, aber Sie wollen nur Ihren Job machen, nicht alles bekommen. Als Arbeitgeber der Klempner sind Sie dafür verantwortlich, Ihre Klempner mit dem auszustatten, was sie benötigen, bevor Sie sie zu den Häusern der Menschen schicken.
Sara
@ Kai Ich verstehe deinen Punkt. In der Software sprechen wir von einer Fabrik, richtig? Aber DI bedeutet normalerweise auch, dass die Klasse keine Fabrik benutzt, da diese immer noch nicht injiziert wird. Sie als Kunde müssten sich an den Arbeitgeber (Fabrik) wenden, um Ihnen die Werkzeuge zu geben, damit Sie an den Klempner übergeben können. Würde es in einem Programm nicht so funktionieren? Während der Kunde (Klasse / Funktion / was auch immer aufrufend) die Werkzeuge nicht beschaffen muss, muss er dennoch der Mittelsmann sein, um sicherzustellen, dass er es vom Arbeitgeber (Fabrik) zum Klempner (injizierte Klasse) schafft.
KingOfAllTrades
1
@KingOfAllTrades: Natürlich muss irgendwann jemand Klempner beschäftigen und ausstatten, oder Sie haben keine Klempner. Aber der Kunde tut es nicht. Der Kunde fragt nur nach einem Klempner und bekommt einen, der bereits mit dem ausgestattet ist, was er für seine Arbeit benötigt. Mit DI haben Sie schließlich noch Code, um die Abhängigkeiten zu erfüllen. Aber Sie trennen es von dem Code, der echte Arbeit leistet. Wenn Sie es in vollem Umfang nutzen, machen Ihre Objekte nur ihre Abhängigkeiten bekannt, und die Erstellung von Objektgraphen erfolgt außerhalb, häufig im Init-Code.
CHao
102

Dies ist die einfachste Erklärung zu Dependency Injection und Dependency Injection Container, die ich je gesehen habe:

Ohne Abhängigkeitsinjektion

  • Anwendung benötigt Foo (zB einen Controller), also:
  • Anwendung erstellt Foo
  • Anwendung ruft Foo auf
    • Foo braucht Bar (zB einen Service), also:
    • Foo schafft Bar
    • Foo ruft Bar an
      • Bar braucht Bim (einen Service, ein Repository, ...), also:
      • Bar erstellt Bim
      • Bar macht etwas

Mit Abhängigkeitsinjektion

  • Anwendung braucht Foo, die Bar braucht, die Bim braucht, also:
  • Anwendung erstellt Bim
  • Anwendung erstellt Bar und gibt es Bim
  • Anwendung erstellt Foo und gibt es Bar
  • Anwendung ruft Foo auf
    • Foo ruft Bar an
      • Bar macht etwas

Verwenden eines Abhängigkeitsinjektionscontainers

  • Anwendung braucht Foo also:
  • Die Anwendung erhält Foo aus dem Container, also:
    • Container erstellt Bim
    • Container erstellt Bar und gibt es Bim
    • Container erstellt Foo und gibt ihm Bar
  • Anwendung ruft Foo auf
    • Foo ruft Bar an
      • Bar macht etwas

Abhängigkeitsinjektion und Abhängigkeitsinjektionsbehälter sind verschiedene Dinge:

  • Dependency Injection ist eine Methode zum Schreiben von besserem Code
  • Ein DI-Container ist ein Tool zum Einfügen von Abhängigkeiten

Sie benötigen keinen Container, um die Abhängigkeitsinjektion durchzuführen. Ein Container kann Ihnen jedoch helfen.

Pmpr
quelle
55

Bedeutet "Abhängigkeitsinjektion" nicht nur die Verwendung parametrisierter Konstruktoren und öffentlicher Setter?

James Shores Artikel zeigt die folgenden Beispiele zum Vergleich .

Konstruktor ohne Abhängigkeitsinjektion:

public class Example { 
  private DatabaseThingie myDatabase; 

  public Example() { 
    myDatabase = new DatabaseThingie(); 
  } 

  public void doStuff() { 
    ... 
    myDatabase.getData(); 
    ... 
  } 
} 

Konstruktor mit Abhängigkeitsinjektion:

public class Example { 
  private DatabaseThingie myDatabase; 

  public Example(DatabaseThingie useThisDatabaseInstead) { 
    myDatabase = useThisDatabaseInstead; 
  }

  public void doStuff() { 
    ... 
    myDatabase.getData(); 
    ... 
  } 
}
JaneGoodall
quelle
Sicherlich möchten Sie in der DI-Version das myDatabase-Objekt nicht im Konstruktor no argument initialisieren? Es scheint keinen Sinn zu haben und würde dazu dienen, eine Ausnahme auszulösen, wenn Sie versuchen würden, DoStuff aufzurufen, ohne den überladenen Konstruktor aufzurufen?
Matt Wilko
Nur wenn new DatabaseThingie()keine gültige myDatabase-Instanz generiert wird.
JaneGoodall
40

Um das Konzept der Abhängigkeitsinjektion einfach zu verstehen. Nehmen wir ein Beispiel für einen Schalter zum Ein- und Ausschalten einer Glühbirne.

Ohne Abhängigkeitsinjektion

Der Switch muss vorher wissen, an welche Lampe ich angeschlossen bin (fest codierte Abhängigkeit). Damit,

Schalter -> PermanentBulb // Schalter ist direkt mit der permanenten Glühbirne verbunden, ein Testen ist nicht einfach möglich

Switch(){
PermanentBulb = new Bulb();
PermanentBulb.Toggle();
}

Mit Abhängigkeitsinjektion

Der Schalter weiß nur, dass ich die Lampe ein- und ausschalten muss, die an mich übergeben wird. Damit,

Schalter -> Bulb1 ODER Bulb2 ODER NightBulb (injizierte Abhängigkeit)

Switch(AnyBulb){ //pass it whichever bulb you like
AnyBulb.Toggle();
}

Ändern James Beispiel für Schalter und Leuchtmittel:

public class SwitchTest { 
  TestToggleBulb() { 
    MockBulb mockbulb = new MockBulb(); 

    // MockBulb is a subclass of Bulb, so we can 
    // "inject" it here: 
    Switch switch = new Switch(mockBulb); 

    switch.ToggleBulb(); 
    mockBulb.AssertToggleWasCalled(); 
  } 
}

public class Switch { 
  private Bulb myBulb; 

  public Switch() { 
    myBulb = new Bulb(); 
  } 

  public Switch(Bulb useThisBulbInstead) { 
    myBulb = useThisBulbInstead; 
  } 

  public void ToggleBulb() { 
    ... 
    myBulb.Toggle(); 
    ... 
  } 
}`
wakqasahmed
quelle
36

Was ist Abhängigkeitsinjektion (DI)?

Wie bereits erwähnt, entfällt durch Dependency Injection (DI) die Verantwortung für die direkte Erstellung und Verwaltung der Lebensdauer anderer Objektinstanzen, von denen unsere Interessenklasse (Verbraucherklasse) abhängig ist (im UML-Sinne ). Diese Instanzen werden stattdessen an unsere Consumer-Klasse übergeben, normalerweise als Konstruktorparameter oder über Eigenschaftssetter (die Verwaltung der Instanzierung des Abhängigkeitsobjekts und die Übergabe an die Consumer-Klasse erfolgt normalerweise über einen IoC- Container (Inversion of Control) , aber das ist ein anderes Thema). .

DI, DIP und SOLID

Insbesondere in dem Paradigma von Robert C Martin SOLID Prinzipien des Gegenstandes Designorientierte , DIist eine der möglichen Implementierungen des Dependency Inversion Principle (DIP) . Das DIP ist das Ddes SOLIDMantras - andere DIP - Implementierungen den Service Locator umfassen, und Plugin - Muster.

Ziel des DIP ist es, enge, konkrete Abhängigkeiten zwischen Klassen zu entkoppeln und stattdessen die Kopplung durch eine Abstraktion zu lösen, die über ein interface, abstract classoder erreicht werden kannpure virtual class in Abhängigkeit von der verwendeten Sprache und Ansatz.

Ohne das DIP ist unser Code (ich habe diese "konsumierende Klasse" genannt) direkt an eine konkrete Abhängigkeit gekoppelt und oft mit der Verantwortung belastet, zu wissen, wie eine Instanz dieser Abhängigkeit erhalten und verwaltet wird, dh konzeptionell:

"I need to create/use a Foo and invoke method `GetBar()`"

Nach Anwendung des DIP wird die Anforderung gelockert, und die Sorge, die Lebensdauer der FooAbhängigkeit zu ermitteln und zu verwalten, wurde beseitigt:

"I need to invoke something which offers `GetBar()`"

Warum DIP (und DI) verwenden?

Das Entkoppeln von Abhängigkeiten zwischen Klassen auf diese Weise ermöglicht das einfache Ersetzen dieser Abhängigkeitsklassen durch andere Implementierungen, die auch die Voraussetzungen der Abstraktion erfüllen (z. B. kann die Abhängigkeit mit einer anderen Implementierung derselben Schnittstelle ausgetauscht werden). Darüber hinaus, wie andere erwähnt haben, möglicherweise die häufigste Grund für die Entkopplung von Klassen über das DIP darin, dass eine konsumierende Klasse isoliert getestet werden kann, da dieselben Abhängigkeiten jetzt gestoppt und / oder verspottet werden können.

Eine Konsequenz von DI ist, dass die Lebensdauerverwaltung von Abhängigkeitsobjektinstanzen nicht mehr von einer konsumierenden Klasse gesteuert wird, da das Abhängigkeitsobjekt jetzt an die konsumierende Klasse übergeben wird (über Konstruktor- oder Setter-Injection).

Dies kann auf verschiedene Arten betrachtet werden:

  • Wenn die Lebensdauerkontrolle von Abhängigkeiten durch die konsumierende Klasse beibehalten werden muss, kann die Kontrolle wiederhergestellt werden, indem eine (abstrakte) Factory zum Erstellen der Abhängigkeitsklasseninstanzen in die Consumer-Klasse eingefügt wird. Der Verbraucher kann bei Bedarf Instanzen über a Createim Werk abrufen und diese Instanzen nach Abschluss entsorgen.
  • Oder die Lebensdauerkontrolle von Abhängigkeitsinstanzen kann an einen IoC-Container übergeben werden (mehr dazu weiter unten).

Wann DI verwenden?

  • Wenn wahrscheinlich eine gleichwertige Implementierung durch eine Abhängigkeit ersetzt werden muss,
  • Jedes Mal, wenn Sie die Methoden einer Klasse isoliert von ihren Abhängigkeiten testen müssen,
  • Wo die Unsicherheit über die Lebensdauer einer Abhängigkeit ein Experimentieren rechtfertigen kann (z. B. Hey, MyDepClassist Thread sicher - was ist, wenn wir es zu einem Singleton machen und allen Verbrauchern dieselbe Instanz injizieren?)

Beispiel

Hier ist eine einfache C # -Implementierung. Angesichts der folgenden Konsumklasse:

public class MyLogger
{
   public void LogRecord(string somethingToLog)
   {
      Console.WriteLine("{0:HH:mm:ss} - {1}", DateTime.Now, somethingToLog);
   }
}

Obwohl es scheinbar harmlos ist, hat es zwei staticAbhängigkeiten von zwei anderen Klassen, System.DateTimeund System.Consolewas nicht nur die Protokollierungsausgabeoptionen einschränkt (die Protokollierung an der Konsole ist wertlos, wenn niemand zuschaut), sondern schlimmer noch, es ist schwierig, angesichts der Abhängigkeit von automatisch zu testen eine nicht deterministische Systemuhr.

Wir können uns jedoch DIPauf diese Klasse beziehen, indem wir das Problem der Zeitstempelung als Abhängigkeit abstrahieren und MyLoggernur an eine einfache Schnittstelle koppeln:

public interface IClock
{
    DateTime Now { get; }
}

Wir können auch die Abhängigkeit von Consoleeiner Abstraktion wie a lösen TextWriter. Die Abhängigkeitsinjektion wird normalerweise entweder als constructorInjektion (Übergabe einer Abstraktion an eine Abhängigkeit als Parameter an den Konstruktor einer konsumierenden Klasse) oder Setter Injection(Übergabe der Abhängigkeit über einen setXyz()Setter oder eine .Net-Eigenschaft mit {set;}definiert) implementiert . Die Konstruktorinjektion wird bevorzugt, da dies garantiert, dass sich die Klasse nach der Erstellung in einem korrekten Zustand befindet und die internen Abhängigkeitsfelder als readonly(C #) oder final(Java) markiert werden können . Wenn wir also die Konstruktorinjektion für das obige Beispiel verwenden, haben wir Folgendes:

public class MyLogger : ILogger // Others will depend on our logger.
{
    private readonly TextWriter _output;
    private readonly IClock _clock;

    // Dependencies are injected through the constructor
    public MyLogger(TextWriter stream, IClock clock)
    {
        _output = stream;
        _clock = clock;
    }

    public void LogRecord(string somethingToLog)
    {
        // We can now use our dependencies through the abstraction 
        // and without knowledge of the lifespans of the dependencies
        _output.Write("{0:yyyy-MM-dd HH:mm:ss} - {1}", _clock.Now, somethingToLog);
    }
}

(Es Clockmuss ein Beton bereitgestellt werden, auf den natürlich zurückgegriffen werden kann DateTime.Now, und die beiden Abhängigkeiten müssen von einem IoC-Container über eine Konstruktorinjektion bereitgestellt werden.)

Es kann ein automatisierter Komponententest erstellt werden, der definitiv beweist, dass unser Logger ordnungsgemäß funktioniert, da wir jetzt die Kontrolle über die Abhängigkeiten - die Zeit - haben und die schriftliche Ausgabe ausspionieren können:

[Test]
public void LoggingMustRecordAllInformationAndStampTheTime()
{
    // Arrange
    var mockClock = new Mock<IClock>();
    mockClock.Setup(c => c.Now).Returns(new DateTime(2015, 4, 11, 12, 31, 45));
    var fakeConsole = new StringWriter();

    // Act
    new MyLogger(fakeConsole, mockClock.Object)
        .LogRecord("Foo");

    // Assert
    Assert.AreEqual("2015-04-11 12:31:45 - Foo", fakeConsole.ToString());
}

Nächste Schritte

Die Abhängigkeitsinjektion ist ausnahmslos mit einem Inversion of Control-Container (IoC) verbunden , um die konkreten Abhängigkeitsinstanzen zu injizieren (bereitzustellen) und Lebensdauerinstanzen zu verwalten. Während des Konfigurations- / Bootstrapping-Prozesses können in IoCContainern folgende Elemente definiert werden:

  • Zuordnung zwischen jeder Abstraktion und der konfigurierten konkreten Implementierung (z. B. "Wenn ein Verbraucher eine anfordert IBar, eine ConcreteBarInstanz zurückgibt" )
  • Es können Richtlinien für die Lebensdauerverwaltung jeder Abhängigkeit eingerichtet werden, z. B. um ein neues Objekt für jede Verbraucherinstanz zu erstellen, eine Singleton-Abhängigkeitsinstanz für alle Verbraucher freizugeben, dieselbe Abhängigkeitsinstanz nur für denselben Thread zu verwenden usw.
  • In .Net kennen IoC-Container Protokolle wie IDisposableund übernehmen die Verantwortung für DisposingAbhängigkeiten gemäß der konfigurierten Lebensdauerverwaltung.

Sobald IoC-Container konfiguriert / gebootet wurden, arbeiten sie normalerweise nahtlos im Hintergrund, sodass sich der Codierer auf den jeweiligen Code konzentrieren kann, anstatt sich um Abhängigkeiten zu kümmern.

Der Schlüssel zu DI-freundlichem Code besteht darin, eine statische Kopplung von Klassen zu vermeiden und new () nicht zum Erstellen von Abhängigkeiten zu verwenden

Wie im obigen Beispiel erfordert das Entkoppeln von Abhängigkeiten einen gewissen Entwurfsaufwand, und für den Entwickler ist ein Paradigmenwechsel erforderlich, um die Gewohnheit, newAbhängigkeiten direkt zu ändern, aufzuheben und stattdessen dem Container zu vertrauen, um Abhängigkeiten zu verwalten.

Es gibt jedoch viele Vorteile, insbesondere in der Möglichkeit, Ihre Interessenklasse gründlich zu testen.

Hinweis : Die Erstellung / Zuordnung / Projektion (über new ..()) von POCO / POJO / Serialisierungs-DTOs / Entitätsdiagrammen / anonymen JSON-Projektionen ua - dh "Nur Daten" -Klassen oder Datensätze -, die von Methoden verwendet oder zurückgegeben werden, gelten nicht als Abhängigkeiten (in der UML-Sinn) und nicht DI unterworfen. Das Projizieren newist in Ordnung.

StuartLC
quelle
1
Das Problem ist DIP! = DI. Bei DIP geht es darum, die Abstraktion von der Implementierung zu entkoppeln: A. High-Level-Module sollten nicht von Low-Level-Modulen abhängen. Beides sollte von Abstraktionen abhängen. B. Abstraktionen sollten nicht von Details abhängen. Details sollten von Abstraktionen abhängen. DI ist eine Möglichkeit, die Objekterstellung von der Objektnutzung zu entkoppeln.
Ricardo Rivaldo
Ja, die Unterscheidung ist in meinem Absatz 2 "DI eine der möglichen Implementierungen von DIP" in Onkel Bobs SOLID-Paradigma klar angegeben . Ich habe dies auch in einem früheren Beitrag klargestellt .
StuartLC
25

Bei Dependency Injection (DI) geht es darum, den Quellcode der Anwendung sauber und stabil zu halten :

  • frei von Abhängigkeitsinitialisierungscode
  • stabil unabhängig von der verwendeten Abhängigkeit

Praktisch trennt jedes Entwurfsmuster Bedenken, damit zukünftige Änderungen die Mindestdateien beeinflussen.

Die spezifische Domäne von DI ist die Delegierung der Abhängigkeitskonfiguration und -initialisierung.

Beispiel: DI mit Shell-Skript

Wenn Sie gelegentlich außerhalb von Java arbeiten, erinnern Sie sich daran, wie sourcehäufig in vielen Skriptsprachen (Shell, Tcl usw. oder sogar importin Python, das für diesen Zweck missbraucht wird) verwendet wird.

Betrachten Sie ein einfaches dependent.shSkript:

#!/bin/sh
# Dependent
touch         "one.txt" "two.txt"
archive_files "one.txt" "two.txt"

Das Skript ist abhängig: Es wird alleine nicht erfolgreich ausgeführt ( archive_filesist nicht definiert).

Sie definieren archive_filesim archive_files_zip.shImplementierungsskript ( zipin diesem Fall):

#!/bin/sh
# Dependency
function archive_files {
    zip files.zip "$@"
}

Anstatt das sourceImplementierungsskript direkt im abhängigen zu verwenden, verwenden Sie einen injector.sh"Container", der beide "Komponenten" umschließt:

#!/bin/sh 
# Injector
source ./archive_files_zip.sh
source ./dependent.sh

Die archive_files Abhängigkeit wird gerade injiziert in abhängige Skript.

Sie könnten eine Abhängigkeit injiziert haben, die die archive_filesVerwendung von taroder implementiert xz.

Beispiel: Entfernen von DI

Wenn das dependent.shSkript Abhängigkeiten direkt verwendet, wird der Ansatz als Abhängigkeitssuche bezeichnet (was der Abhängigkeitsinjektion entgegengesetzt ist ):

#!/bin/sh
# Dependent

# dependency look-up
source ./archive_files_zip.sh

touch         "one.txt" "two.txt"
archive_files "one.txt" "two.txt"

Das Problem ist nun, dass die abhängige "Komponente" die Initialisierung selbst durchführen muss.

Der Quellcode der "Komponente" ist weder sauber noch stabil, da jede Änderung bei der Initialisierung von Abhängigkeiten eine neue Version auch für die Quellcodedatei der "Komponente" erfordert.

Letzte Worte

DI wird nicht so stark betont und populär gemacht wie in Java-Frameworks.

Aber es ist ein generischer Ansatz, um Bedenken aufzuteilen:

  • Anwendungsentwicklung ( Single Source Code Release Lebenszyklus)
  • Anwendungsbereitstellung ( mehr Zielumgebungen mit unabhängigen Lifecycles)

Konfiguration nur mit Abhängigkeitssuche verwenden hilft nicht, da sich die Anzahl der Konfigurationsparameter pro Abhängigkeit ändern kann (z. B. neuer Authentifizierungstyp) sowie die Anzahl der unterstützten Arten von Abhängigkeiten (z. B. neuer Datenbanktyp).

uvsmtid
quelle
Ich würde die Fähigkeit hinzufügen, eine bestimmte Klasse (Testen) zu vervollständigen, ohne ihre Abhängigkeiten vervollständigen zu müssen, als Zweck für DI.
David
22

Alle oben genannten Antworten sind gut. Mein Ziel ist es, das Konzept auf einfache Weise zu erklären, damit jeder ohne Programmierkenntnisse auch das Konzept verstehen kann

Die Abhängigkeitsinjektion ist eines der Entwurfsmuster, mit denen wir komplexe Systeme auf einfachere Weise erstellen können.

Wir können eine Vielzahl von Anwendungen dieses Musters in unserem täglichen Leben sehen. Einige Beispiele sind Kassettenrekorder, VCD, CD-Laufwerk usw.

Tragbares Tonbandgerät, Mitte des 20. Jahrhunderts.

Das obige Bild ist ein Bild eines tragbaren Tonbandgeräts von Rolle zu Rolle, Mitte des 20. Jahrhunderts. Quelle .

Die Hauptabsicht eines Tonbandgeräts besteht darin, Ton aufzunehmen oder wiederzugeben.

Beim Entwerfen eines Systems ist eine Rolle erforderlich, um Ton oder Musik aufzunehmen oder wiederzugeben. Es gibt zwei Möglichkeiten, dieses System zu entwerfen

  1. Wir können die Rolle in die Maschine legen
  2. Wir können einen Haken für die Rolle bereitstellen, an dem sie platziert werden kann.

Wenn wir die erste verwenden, müssen wir die Maschine öffnen, um die Rolle zu wechseln. Wenn wir uns für die zweite entscheiden, bei der ein Haken für die Rolle platziert wird, erhalten wir einen zusätzlichen Vorteil beim Abspielen von Musik, indem wir die Rolle wechseln. und auch die Funktion nur auf das Spielen von irgendetwas auf der Rolle reduzieren.

Ebenso wie die weise Abhängigkeitsinjektion ist der Prozess der Externalisierung der Abhängigkeiten, um sich nur auf die spezifische Funktionalität der Komponente zu konzentrieren, so dass unabhängige Komponenten miteinander verbunden werden können, um ein komplexes System zu bilden.

Die Hauptvorteile, die wir durch die Verwendung der Abhängigkeitsinjektion erzielt haben.

  • Hoher Zusammenhalt und lose Kopplung.
  • Abhängigkeit externalisieren und nur auf Verantwortung schauen.
  • Dinge als Komponenten herstellen und zu großen Systemen mit hohen Fähigkeiten kombinieren.
  • Es hilft, qualitativ hochwertige Komponenten zu entwickeln, da diese unabhängig voneinander entwickelt und ordnungsgemäß getestet werden.
  • Es hilft, die Komponente durch eine andere zu ersetzen, wenn eine ausfällt.

Heute bildet dieses Konzept die Grundlage für bekannte Frameworks in der Programmierwelt. Der Spring Angular usw. sind die bekannten Software-Frameworks, die auf diesem Konzept aufbauen

Die Abhängigkeitsinjektion ist ein Muster, das zum Erstellen von Instanzen von Objekten verwendet wird, auf die sich andere Objekte stützen, ohne zur Kompilierungszeit zu wissen, welche Klasse verwendet wird, um diese Funktionalität bereitzustellen, oder einfach die Art und Weise, wie Eigenschaften in ein Objekt eingefügt werden, als Abhängigkeitsinjektion bezeichnet wird.

Beispiel für die Abhängigkeitsinjektion

Zuvor haben wir Code wie diesen geschrieben

Public MyClass{
 DependentClass dependentObject
 /*
  At somewhere in our code we need to instantiate 
  the object with new operator  inorder to use it or perform some method.
  */ 
  dependentObject= new DependentClass();
  dependentObject.someMethod();
}

Mit der Abhängigkeitsinjektion nimmt der Abhängigkeitsinjektor die Instanziierung für uns ab

Public MyClass{
 /* Dependency injector will instantiate object*/
 DependentClass dependentObject

 /*
  At somewhere in our code we perform some method. 
  The process of  instantiation will be handled by the dependency injector
 */ 

  dependentObject.someMethod();
}

Sie können auch lesen

Unterschied zwischen Inversion der Kontrolle und Abhängigkeitsinjektion

samuelj90
quelle
17

Was ist Abhängigkeitsinjektion?

Dependency Injection (DI) bedeutet, die voneinander abhängigen Objekte zu entkoppeln. Angenommen, Objekt A ist von Objekt B abhängig, daher besteht die Idee darin, diese Objekte voneinander zu entkoppeln. Wir müssen das Objekt nicht mit einem neuen Schlüsselwort fest codieren, sondern müssen zur Laufzeit trotz Kompilierungszeit Abhängigkeiten mit Objekten teilen. Wenn wir darüber reden

So funktioniert Dependency Injection im Frühjahr:

Wir müssen das Objekt nicht mit einem neuen Schlüsselwort fest codieren, sondern müssen die Bean-Abhängigkeit in der Konfigurationsdatei definieren. Der Federbehälter ist für das Anschließen aller verantwortlich.

Inversion der Kontrolle (IOC)

IOC ist ein allgemeines Konzept und kann auf viele verschiedene Arten ausgedrückt werden. Die Abhängigkeitsinjektion ist ein konkretes Beispiel für IOC.

Zwei Arten der Abhängigkeitsinjektion:

  1. Konstruktorinjektion
  2. Setter-Injektion

1. Konstruktorbasierte Abhängigkeitsinjektion:

Konstruktorbasiertes DI wird erreicht, wenn der Container einen Klassenkonstruktor mit einer Reihe von Argumenten aufruft, die jeweils eine Abhängigkeit von einer anderen Klasse darstellen.

public class Triangle {

private String type;

public String getType(){
    return type;
 }

public Triangle(String type){   //constructor injection
    this.type=type;
 }
}
<bean id=triangle" class ="com.test.dependencyInjection.Triangle">
        <constructor-arg value="20"/>
  </bean>

2. Setter-basierte Abhängigkeitsinjektion:

Setter-basierte DI wird durch den Container erreicht, der Setter-Methoden für Ihre Beans aufruft, nachdem ein Konstruktor ohne Argumente oder eine statische Factory-Methode ohne Argumente aufgerufen wurde, um Ihre Bean zu instanziieren.

public class Triangle{

 private String type;

 public String getType(){
    return type;
  }
 public void setType(String type){          //setter injection
    this.type = type;
  }
 }

<!-- setter injection -->
 <bean id="triangle" class="com.test.dependencyInjection.Triangle">
        <property name="type" value="equivialteral"/>

ANMERKUNG: Es ist eine gute Faustregel, Konstruktorargumente für obligatorische Abhängigkeiten und Setter für optionale Abhängigkeiten zu verwenden. Beachten Sie, dass die, wenn wir eine Annotation verwenden, die auf der Annotation @Required für einen Setter basiert, verwendet werden kann, um Setter als erforderliche Abhängigkeiten zu erstellen.

Harleen
quelle
15

Die beste Analogie, die ich mir vorstellen kann, ist der Chirurg und seine Assistenten in einem Operationssaal, wo der Chirurg die Hauptperson und sein Assistent ist, der die verschiedenen chirurgischen Komponenten bereitstellt, wenn er sie benötigt, damit sich der Chirurg auf die eine konzentrieren kann was er am besten kann (Operation). Ohne den Assistenten muss der Chirurg die Komponenten jedes Mal selbst besorgen, wenn er eine benötigt.

Kurz gesagt, DI ist eine Technik, mit der eine gemeinsame zusätzliche Verantwortung (Belastung) für Komponenten zum Abrufen der abhängigen Komponenten beseitigt wird, indem diese bereitgestellt werden.

DI bringt Sie dem Prinzip der Einzelverantwortung (Single Responsibility, SR) näher, wie dem surgeon who can concentrate on surgery.

Verwendung von DI: Ich würde die Verwendung von DI in fast allen Produktionsprojekten (klein / groß) empfehlen, insbesondere in sich ständig ändernden Geschäftsumgebungen :)

Warum: Weil Sie möchten, dass Ihr Code leicht testbar, verspottbar usw. ist, damit Sie Ihre Änderungen schnell testen und auf den Markt bringen können. Außerdem, warum sollten Sie nicht, wenn es viele tolle kostenlose Tools / Frameworks gibt, die Sie auf Ihrem Weg zu einer Codebasis unterstützen, auf der Sie mehr Kontrolle haben.

Anwar Husain
quelle
@ WindRider Danke. Ich kann nicht mehr zustimmen. Das menschliche Leben und der menschliche Körper sind großartige Beispiele für herausragendes Design. Die Wirbelsäule ist ein hervorragendes Beispiel für einen ESB :) ...
Anwar Husain
15

Beispiel, wir haben 2 Klassen Clientund Service. Clientwird benutzenService

public class Service {
    public void doSomeThingInService() {
        // ...
    }
}

Ohne Abhängigkeitsinjektion

Weg 1)

public class Client {
    public void doSomeThingInClient() {
        Service service = new Service();
        service.doSomeThingInService();
    }
}

Weg 2)

public class Client {
    Service service = new Service();
    public void doSomeThingInClient() {
        service.doSomeThingInService();
    }
}

Weg 3)

public class Client {
    Service service;
    public Client() {
        service = new Service();
    }
    public void doSomeThingInClient() {
        service.doSomeThingInService();
    }
}

1) 2) 3) Verwenden

Client client = new Client();
client.doSomeThingInService();

Vorteile

  • Einfach

Nachteile

  • Hart für ClientTestklasse
  • Wenn wir den ServiceKonstruktor ändern , müssen wir den Code an allen Stellen ändern, an denen das ServiceObjekt erstellt wird

Verwenden Sie die Abhängigkeitsinjektion

Weg 1) Konstruktorinjektion

public class Client {
    Service service;

    Client(Service service) {
        this.service = service;
    }

    // Example Client has 2 dependency 
    // Client(Service service, IDatabas database) {
    //    this.service = service;
    //    this.database = database;
    // }

    public void doSomeThingInClient() {
        service.doSomeThingInService();
    }
}

Verwenden von

Client client = new Client(new Service());
// Client client = new Client(new Service(), new SqliteDatabase());
client.doSomeThingInClient();

Weg 2) Setter-Injektion

public class Client {
    Service service;

    public void setService(Service service) {
        this.service = service;
    }

    public void doSomeThingInClient() {
        service.doSomeThingInService();
    }
}

Verwenden von

Client client = new Client();
client.setService(new Service());
client.doSomeThingInClient();

Weg 3) Schnittstelleninjektion

Überprüfen Sie https://en.wikipedia.org/wiki/Dependency_injection

===

Dieser Code folgt bereits Dependency Injectionund ist für die Testklasse einfacher Client.
Wir verwenden jedoch immer noch new Service()viel Zeit und es ist nicht gut, den ServiceKonstruktor zu ändern . Um dies zu verhindern, können wir DI-Injektoren wie
1) Einfaches Handbuch verwendenInjector

public class Injector {
    public static Service provideService(){
        return new Service();
    }

    public static IDatabase provideDatatBase(){
        return new SqliteDatabase();
    }
    public static ObjectA provideObjectA(){
        return new ObjectA(provideService(...));
    }
}

Verwenden von

Service service = Injector.provideService();

2) Bibliothek verwenden: Für Android Dolch2

Vorteile

  • Machen Sie den Test einfacher
  • Wenn Sie das ändern Service, müssen Sie es nur in der Injector-Klasse ändern
  • Wenn Sie use verwenden Constructor Injection, sehen Sie beim Betrachten des Konstruktors von Client, wie viele Abhängigkeiten der ClientKlasse vorhanden sind

Nachteile

  • Wenn Sie use verwenden Constructor Injection, wird das ServiceObjekt beim ClientErstellen erstellt. Manchmal verwenden wir Funktionen in der ClientKlasse, ohne dass die Serviceso erstellte Verwendung Serviceverschwendet wird

Definition der Abhängigkeitsinjektion

https://en.wikipedia.org/wiki/Dependency_injection

Eine Abhängigkeit ist ein Objekt, das verwendet werden kann ( Service)
Eine Injektion ist die Übergabe einer Abhängigkeit ( Service) an ein abhängiges Objekt ( Client), das sie verwenden würde

Phan Van Linh
quelle
13

Dies bedeutet, dass Objekte nur so viele Abhängigkeiten haben sollten, wie für ihre Arbeit erforderlich sind, und dass die Abhängigkeiten gering sein sollten. Darüber hinaus sollten die Abhängigkeiten eines Objekts nach Möglichkeit von Schnittstellen und nicht von „konkreten“ Objekten abhängen. (Ein konkretes Objekt ist jedes Objekt, das mit dem Schlüsselwort new erstellt wurde.) Die lose Kopplung fördert eine bessere Wiederverwendbarkeit, eine einfachere Wartbarkeit und ermöglicht die einfache Bereitstellung von Scheinobjekten anstelle teurer Dienste.

Die "Dependency Injection" (DI) ist auch als "Inversion of Control" (IoC) bekannt und kann als Technik zur Förderung dieser losen Kopplung verwendet werden.

Es gibt zwei Hauptansätze zur Implementierung von DI:

  1. Konstruktorinjektion
  2. Setter-Injektion

Konstruktorinjektion

Es ist die Technik, Objektabhängigkeiten an seinen Konstruktor zu übergeben.

Beachten Sie, dass der Konstruktor eine Schnittstelle und kein konkretes Objekt akzeptiert. Beachten Sie außerdem, dass eine Ausnahme ausgelöst wird, wenn der Parameter orderDao null ist. Dies unterstreicht die Wichtigkeit, eine gültige Abhängigkeit zu erhalten. Die Konstruktorinjektion ist meiner Meinung nach der bevorzugte Mechanismus, um einem Objekt seine Abhängigkeiten zu geben. Dem Entwickler ist beim Aufrufen des Objekts klar, welche Abhängigkeiten dem Objekt "Person" für eine ordnungsgemäße Ausführung zugewiesen werden müssen.

Setter-Injektion

Betrachten Sie jedoch das folgende Beispiel… Angenommen, Sie haben eine Klasse mit zehn Methoden, die keine Abhängigkeiten aufweisen, aber Sie fügen eine neue Methode hinzu, die von IDAO abhängig ist. Sie können den Konstruktor so ändern, dass er die Konstruktorinjektion verwendet. Dies kann Sie jedoch dazu zwingen, Änderungen an allen Konstruktoraufrufen überall vorzunehmen. Alternativ können Sie einfach einen neuen Konstruktor hinzufügen, der die Abhängigkeit übernimmt. Wie kann ein Entwickler dann leicht erkennen, wann ein Konstruktor über dem anderen verwendet werden muss? Wenn die Erstellung der Abhängigkeit sehr teuer ist, warum sollte sie erstellt und an den Konstruktor übergeben werden, wenn sie möglicherweise nur selten verwendet wird? "Setter Injection" ist eine weitere DI-Technik, die in solchen Situationen eingesetzt werden kann.

Setter Injection erzwingt nicht, dass Abhängigkeiten an den Konstruktor übergeben werden. Stattdessen werden die Abhängigkeiten auf öffentliche Eigenschaften festgelegt, die vom benötigten Objekt verfügbar gemacht werden. Wie bereits angedeutet, sind die Hauptmotivatoren dafür:

  1. Unterstützung der Abhängigkeitsinjektion, ohne den Konstruktor einer Legacy-Klasse ändern zu müssen.
  2. Ermöglichen, dass teure Ressourcen oder Dienste so spät wie möglich und nur bei Bedarf erstellt werden.

Hier ist das Beispiel, wie der obige Code aussehen würde:

public class Person {
    public Person() {}

    public IDAO Address {
        set { addressdao = value; }
        get {
            if (addressdao == null)
              throw new MemberAccessException("addressdao" +
                             " has not been initialized");
            return addressdao;
        }
    }

    public Address GetAddress() {
       // ... code that uses the addressdao object
       // to fetch address details from the datasource ...
    }

    // Should not be called directly;
    // use the public property instead
    private IDAO addressdao;
Piyush Deshpande
quelle
3
Ich denke, Ihr erster Absatz weicht von der Frage ab und ist überhaupt nicht die Definition von DI (dh Sie versuchen, SOLID zu definieren, nicht DI). Technisch gesehen können Sie auch bei 100 Abhängigkeiten die Abhängigkeitsinjektion verwenden. Ebenso ist es möglich, konkrete Abhängigkeiten einzufügen - es handelt sich immer noch um Abhängigkeitsinjektion.
Jay Sullivan
10

Ich denke, da jeder für DI geschrieben hat, möchte ich ein paar Fragen stellen.

  1. Wenn Sie eine DI-Konfiguration haben, in der alle tatsächlichen Implementierungen (keine Schnittstellen), die in eine Klasse eingefügt werden sollen (z. B. Dienste für einen Controller), warum ist das keine Hardcodierung?
  2. Was ist, wenn ich das Objekt zur Laufzeit ändern möchte? In meiner Konfiguration steht beispielsweise bereits, wenn ich MyController instanziiere, für FileLogger als ILogger injizieren. Aber ich möchte vielleicht DatabaseLogger injizieren.
  3. Jedes Mal, wenn ich ändern möchte, welche Objekte meine AClass benötigt, muss ich jetzt zwei Stellen untersuchen - die Klasse selbst und die Konfigurationsdatei. Wie macht das das Leben leichter?
  4. Wenn Aproperty of AClass nicht injiziert wird, ist es schwieriger, es zu verspotten?
  5. Zurück zur ersten Frage. Wenn die Verwendung von new object () schlecht ist, warum injizieren wir dann die Implementierung und nicht die Schnittstelle? Ich denke, viele von Ihnen sagen, dass wir die Schnittstelle tatsächlich injizieren, aber die Konfiguration lässt Sie die Implementierung dieser Schnittstelle angeben. Nicht zur Laufzeit. Sie ist während der Kompilierungszeit fest codiert.

Dies basiert auf der Antwort @Adam N geschrieben.

Warum muss sich PersonService nicht mehr um GroupMembershipService kümmern? Sie haben gerade erwähnt, dass GroupMembership mehrere Dinge (Objekte / Eigenschaften) hat, von denen es abhängt. Wenn GMService in PService erforderlich wäre, hätten Sie es als Eigenschaft. Sie können das verspotten, unabhängig davon, ob Sie es injiziert haben oder nicht. Ich möchte nur, dass es injiziert wird, wenn GMService spezifischere untergeordnete Klassen hat, die Sie erst zur Laufzeit kennen. Dann möchten Sie die Unterklasse einfügen. Oder wenn Sie das entweder als Singleton oder als Prototyp verwenden möchten. Um ehrlich zu sein, ist in der Konfigurationsdatei alles fest codiert, bis zu welcher Unterklasse für einen Typ (Schnittstelle), die während der Kompilierungszeit eingefügt werden soll.

BEARBEITEN

Ein schöner Kommentar von Jose Maria Arranz zu DI

DI erhöht die Kohäsion, indem es nicht mehr erforderlich ist, die Abhängigkeitsrichtung zu bestimmen und einen Klebercode zu schreiben.

Falsch. Die Richtung der Abhängigkeiten ist in XML-Form oder als Anmerkungen. Ihre Abhängigkeiten werden als XML-Code und Anmerkungen geschrieben. XML und Anmerkungen sind Quellcode.

DI reduziert die Kopplung, indem alle Ihre Komponenten modular (dh austauschbar) und mit genau definierten Schnittstellen zueinander ausgestattet werden.

Falsch. Sie benötigen kein DI-Framework, um einen modularen Code basierend auf Schnittstellen zu erstellen.

Informationen zum Ersetzen: Mit einem sehr einfachen .properties-Archiv und Class.forName können Sie definieren, welche Klassen geändert werden können. Wenn eine Klasse Ihres Codes geändert werden kann, ist Java nichts für Sie. Verwenden Sie eine Skriptsprache. Übrigens: Anmerkungen können nicht ohne Neukompilieren geändert werden.

Meiner Meinung nach gibt es nur einen Grund für DI-Gerüste: die Reduzierung der Kesselplatte. Mit einem gut gemachten Factory-System können Sie dasselbe tun, kontrollierter und vorhersehbarer als Ihr bevorzugtes DI-Framework. DI-Frameworks versprechen eine Reduzierung des Codes (XML und Anmerkungen sind ebenfalls Quellcode). Das Problem ist, dass diese Reduzierung der Kesselplatte nur in sehr sehr einfachen Fällen (eine Instanz pro Klasse und ähnlich) real ist. In der realen Welt ist die Auswahl des geeigneten Serviceobjekts manchmal nicht so einfach wie die Zuordnung einer Klasse zu einem Singleton-Objekt.

Chookoos
quelle
8

Die gängigen Antworten sind nicht hilfreich, da sie die Abhängigkeitsinjektion auf eine Weise definieren, die nicht nützlich ist. Lassen Sie uns zustimmen, dass wir unter "Abhängigkeit" ein bereits vorhandenes anderes Objekt verstehen, das unser Objekt X benötigt. Aber wir sagen nicht, dass wir "Abhängigkeitsinjektion" machen, wenn wir sagen

$foo = Foo->new($bar);

Wir nennen das nur die Übergabe von Parametern an den Konstruktor. Das machen wir regelmäßig, seit die Konstrukteure erfunden wurden.

"Abhängigkeitsinjektion" wird als eine Art "Umkehrung der Steuerung" angesehen, was bedeutet, dass dem Anrufer eine gewisse Logik entzogen wird. Dies ist nicht der Fall, wenn der Aufrufer Parameter übergibt. Wenn dies also DI wäre, würde DI keine Umkehrung der Steuerung bedeuten.

DI bedeutet, dass zwischen dem Aufrufer und dem Konstruktor eine Zwischenebene besteht, die Abhängigkeiten verwaltet. Ein Makefile ist ein einfaches Beispiel für die Abhängigkeitsinjektion. Der "Aufrufer" ist die Person, die in der Befehlszeile "make bar" eingibt, und der "Konstruktor" ist der Compiler. Das Makefile gibt an, dass der Balken von foo abhängt, und es tut a

gcc -c foo.cpp; gcc -c bar.cpp

bevor Sie a

gcc foo.o bar.o -o bar

Die Person, die "make bar" eingibt, muss nicht wissen, dass der Balken von foo abhängt. Die Abhängigkeit wurde zwischen "make bar" und gcc eingefügt.

Der Hauptzweck der Zwischenebene besteht nicht nur darin, die Abhängigkeiten an den Konstruktor zu übergeben, sondern alle Abhängigkeiten an nur einer Stelle aufzulisten und sie vor dem Codierer zu verbergen (nicht, dass der Codierer sie bereitstellt).

Normalerweise stellt die Zwischenebene Fabriken für die erstellten Objekte bereit, die eine Rolle bereitstellen müssen, die jeder angeforderte Objekttyp erfüllen muss. Das liegt daran, dass Sie durch eine Zwischenstufe, die die Details der Konstruktion verbirgt, bereits die von Fabriken verhängte Abstraktionsstrafe erhalten haben. Sie können also auch Fabriken verwenden.

Phil Goetz
quelle
8

Abhängigkeitsinjektion bedeutet eine Möglichkeit (eigentlich auf irgendeine Weise ) für einen Teil des Codes (z. B. eine Klasse), modular auf Abhängigkeiten (andere Teile des Codes, z. B. andere Klassen, von denen es abhängt) zuzugreifen, ohne dass diese fest codiert werden (so) Sie können sich ändern oder frei überschreiben oder bei Bedarf sogar zu einem anderen Zeitpunkt geladen werden.

(und ps, ja, es ist ein überhypter 25 $ Name für ein ziemlich einfaches Konzept geworden) , meine .25Cent

Nikos M.
quelle
8

Ich weiß, dass es bereits viele Antworten gibt, aber ich fand dies sehr hilfreich: http://tutorials.jenkov.com/dependency-injection/index.html

Keine Abhängigkeit:

public class MyDao {

  protected DataSource dataSource = new DataSourceImpl(
    "driver", "url", "user", "password");

  //data access methods...
  public Person readPerson(int primaryKey) {...}     
}

Abhängigkeit:

public class MyDao {

  protected DataSource dataSource = null;

  public MyDao(String driver, String url, String user, String password) {
    this.dataSource = new DataSourceImpl(driver, url, user, password);
  }

  //data access methods...
  public Person readPerson(int primaryKey) {...}
}

Beachten Sie, wie die DataSourceImplInstanziierung in einen Konstruktor verschoben wird. Der Konstruktor verwendet vier Parameter, die die vier Werte sind, die von der benötigt werden DataSourceImpl. Obwohl die MyDaoKlasse immer noch von diesen vier Werten abhängt, erfüllt sie diese Abhängigkeiten selbst nicht mehr. Sie werden von jeder Klasse bereitgestellt, die eine MyDaoInstanz erstellt.

Ali Issa
quelle
1
Würde DI Sie nicht an der Schnittstelle übergeben, die Ihr DataSourceImp bereits erstellt hat?
PmanAce
6

Die Abhängigkeitsinjektion ist eine mögliche Lösung für die allgemein als "Abhängigkeitsverschleierung" bezeichnete Anforderung. Die Verschleierung von Abhängigkeiten ist eine Methode, um die "offensichtliche" Natur aus dem Prozess der Bereitstellung einer Abhängigkeit für eine Klasse herauszunehmen, die dies erfordert, und daher die Bereitstellung dieser Abhängigkeit für diese Klasse in irgendeiner Weise zu verschleiern. Das ist nicht unbedingt eine schlechte Sache. In der Tat ist durch Verschleierung der Art und Weise, in der eine Abhängigkeit für eine Klasse bereitgestellt wird, etwas außerhalb der Klasse für die Erstellung der Abhängigkeit verantwortlich, was bedeutet, dass in verschiedenen Szenarien der Klasse eine unterschiedliche Implementierung der Abhängigkeit bereitgestellt werden kann, ohne dass Änderungen vorgenommen werden zur Klasse. Dies ist ideal, um zwischen Produktions- und Testmodus zu wechseln (z. B. mithilfe einer "Mock" -Dienstabhängigkeit).

Leider ist der schlechte Teil, dass einige Leute angenommen haben, dass Sie ein spezielles Framework benötigen, um Abhängigkeiten zu verschleiern, und dass Sie irgendwie ein "geringerer" Programmierer sind, wenn Sie sich dafür entscheiden, kein bestimmtes Framework dafür zu verwenden. Ein anderer, äußerst beunruhigender Mythos, an den viele glauben, ist, dass die Abhängigkeitsinjektion der einzige Weg ist, um eine Verschleierung der Abhängigkeit zu erreichen. Dies ist nachweislich und historisch und offensichtlich zu 100% falsch, aber Sie werden Schwierigkeiten haben, einige Leute davon zu überzeugen, dass es Alternativen zur Abhängigkeitsinjektion für Ihre Anforderungen zur Verschleierung von Abhängigkeiten gibt.

Programmierer haben die Anforderung der Verschleierung von Abhängigkeiten seit Jahren verstanden und viele alternative Lösungen haben sich sowohl vor als auch nach der Konzeption der Abhängigkeitsinjektion entwickelt. Es gibt Factory-Muster, aber es gibt auch viele Optionen, die ThreadLocal verwenden, bei denen keine Injektion in eine bestimmte Instanz erforderlich ist - die Abhängigkeit wird effektiv in den Thread injiziert, was den Vorteil hat, dass das Objekt verfügbar gemacht wird (über bequeme statische Getter-Methoden) alleKlasse, die dies erfordert, ohne Anmerkungen zu den Klassen hinzufügen zu müssen, die dies erfordern, und komplizierten XML-Kleber einzurichten, um dies zu ermöglichen. Wenn Ihre Abhängigkeiten für die Persistenz erforderlich sind (JPA / JDO oder was auch immer), können Sie die "tranaparente Persistenz" viel einfacher und mit Domänenmodell- und Geschäftsmodellklassen erreichen, die ausschließlich aus POJOs bestehen (dh kein Framework spezifisch / in Anmerkungen eingeschlossen).

Volksman
quelle
5

Bevor Sie zur technischen Beschreibung gehen, visualisieren Sie sie zunächst anhand eines Beispiels aus der Praxis, da Sie viele technische Informationen zum Erlernen der Abhängigkeitsinjektion finden, aber die maximale Zeit, in der Leute wie ich das Kernkonzept nicht verstehen können.

Nehmen Sie im ersten Bild an, dass Sie eine Autofabrik mit vielen Einheiten haben. Ein Auto ist tatsächlich in der Montageeinheit eingebaut , benötigt jedoch Motor , Sitze sowie Räder . Die Montageeinheit ist also von allen Einheiten abhängig und sie sind die Abhängigkeiten der Fabrik.

Sie können spüren, dass es jetzt zu kompliziert ist, alle Aufgaben in dieser Fabrik zu warten, da Sie sich neben der Hauptaufgabe (Zusammenbau des Autos in der Montageeinheit) auch auf andere Einheiten konzentrieren müssen . Die Wartung ist jetzt sehr kostspielig und das Fabrikgebäude ist riesig, sodass Sie Ihr zusätzliches Geld für die Miete benötigen.

Schauen Sie sich jetzt das zweite Bild an. Wenn Sie Anbieter finden, die Ihnen Rad , Sitz und Motor billiger als Ihre Eigenproduktionskosten anbieten, müssen Sie diese jetzt nicht mehr in Ihrer Fabrik herstellen. Sie können jetzt ein kleineres Gebäude nur für Ihre Montageeinheit mieten was Ihre Wartungsaufgabe verringert und Ihre zusätzlichen Mietkosten senkt. Jetzt können Sie sich auch nur noch auf Ihre Hauptaufgabe konzentrieren (Fahrzeugmontage).

Jetzt können wir sagen, dass alle Abhängigkeiten für die Montage eines Autos von den Anbietern im Werk eingespritzt werden . Es ist ein Beispiel für eine reale Abhängigkeitsinjektion (DI) .

Im technischen Wort ist die Abhängigkeitsinjektion eine Technik, bei der ein Objekt (oder eine statische Methode) die Abhängigkeiten eines anderen Objekts liefert. Das Übertragen der Aufgabe, das Objekt zu erstellen, an eine andere Person und die direkte Verwendung der Abhängigkeit wird als Abhängigkeitsinjektion bezeichnet.

Dies wird Ihnen jetzt helfen, DI mit einem technischen Wort zu lernen. Dies zeigt an, wann DI verwendet werden soll und wann nicht .

Alles in einer Autofabrik.

Einfache Autofabrik

Gk Mohammad Emon
quelle
1
klarste Antwort von den 40 ish. Beispiel aus dem wirklichen Leben und Bilder. +1. Sollte die akzeptierte Antwort sein.
Marche Remi
4

aus Book Apress.Spring.Persistence.with.Hibernate.Oct.2010

Der Zweck der Abhängigkeitsinjektion besteht darin, die Arbeit zum Auflösen externer Softwarekomponenten von Ihrer Geschäftslogik für Anwendungen zu entkoppeln. Ohne Abhängigkeitsinjektion können die Details, wie eine Komponente auf erforderliche Dienste zugreift, mit dem Code der Komponente verwechselt werden. Dies erhöht nicht nur das Fehlerpotential, erhöht das Aufblähen des Codes und vergrößert die Komplexität der Wartung. Es koppelt Komponenten enger miteinander, was es schwierig macht, Abhängigkeiten beim Refactoring oder Testen zu ändern.

BERGUIGA Mohamed Amine
quelle
4

Dependency Injection (DI) ist eine von Design Patterns, die die Grundfunktion von OOP verwendet - die Beziehung in einem Objekt zu einem anderen Objekt. Während die Vererbung ein Objekt erbt, um ein anderes Objekt komplexer und spezifischer zu gestalten, erstellt eine Beziehung oder Zuordnung einfach einen Zeiger auf ein anderes Objekt von einem Objekt mithilfe eines Attributs. Die Leistung von DI wird mit anderen Funktionen von OOP kombiniert, ebenso wie Schnittstellen und versteckter Code. Angenommen, wir haben einen Kunden (Abonnenten) in der Bibliothek, der der Einfachheit halber nur ein Buch ausleihen kann.

Schnittstelle des Buches:

package com.deepam.hidden;

public interface BookInterface {

public BookInterface setHeight(int height);
public BookInterface setPages(int pages);   
public int getHeight();
public int getPages();  

public String toString();
}

Als nächstes können wir viele Arten von Büchern haben; Eine der Arten ist Fiktion:

package com.deepam.hidden;

public class FictionBook implements BookInterface {
int height = 0; // height in cm
int pages = 0; // number of pages

/** constructor */
public FictionBook() {
    // TODO Auto-generated constructor stub
}

@Override
public FictionBook setHeight(int height) {
  this.height = height;
  return this;
}

@Override
public FictionBook setPages(int pages) {
  this.pages = pages;
  return this;      
}

@Override
public int getHeight() {
    // TODO Auto-generated method stub
    return height;
}

@Override
public int getPages() {
    // TODO Auto-generated method stub
    return pages;
}

@Override
public String toString(){
    return ("height: " + height + ", " + "pages: " + pages);
}
}

Jetzt kann der Abonnent eine Zuordnung zum Buch haben:

package com.deepam.hidden;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class Subscriber {
BookInterface book;

/** constructor*/
public Subscriber() {
    // TODO Auto-generated constructor stub
}

// injection I
public void setBook(BookInterface book) {
    this.book = book;
}

// injection II
public BookInterface setBook(String bookName) {
    try {
        Class<?> cl = Class.forName(bookName);
        Constructor<?> constructor = cl.getConstructor(); // use it for parameters in constructor
        BookInterface book = (BookInterface) constructor.newInstance();
        //book = (BookInterface) Class.forName(bookName).newInstance();
    } catch (InstantiationException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    } catch (SecurityException e) {
        e.printStackTrace();
    } catch (IllegalArgumentException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    }
    return book;
}

public BookInterface getBook() {
  return book;
}

public static void main(String[] args) {

}

}

Alle drei Klassen können für die eigene Implementierung ausgeblendet werden. Jetzt können wir diesen Code für DI verwenden:

package com.deepam.implement;

import com.deepam.hidden.Subscriber;
import com.deepam.hidden.FictionBook;

public class CallHiddenImplBook {

public CallHiddenImplBook() {
    // TODO Auto-generated constructor stub
}

public void doIt() {
    Subscriber ab = new Subscriber();

    // injection I
    FictionBook bookI = new FictionBook();
    bookI.setHeight(30); // cm
    bookI.setPages(250);
    ab.setBook(bookI); // inject
    System.out.println("injection I " + ab.getBook().toString());

    // injection II
    FictionBook bookII = ((FictionBook) ab.setBook("com.deepam.hidden.FictionBook")).setHeight(5).setPages(108); // inject and set
    System.out.println("injection II " + ab.getBook().toString());      
}

public static void main(String[] args) {
    CallHiddenImplBook kh = new CallHiddenImplBook();
    kh.doIt();
}
}

Es gibt viele verschiedene Möglichkeiten, die Abhängigkeitsinjektion zu verwenden. Es ist möglich, es mit Singleton usw. zu kombinieren, aber im Grunde ist es nur eine Assoziation, die durch Erstellen eines Attributs vom Objekttyp in einem anderen Objekt realisiert wird. Der Nutzen ist nur und nur in der Eigenschaft, dass der Code, den wir immer wieder schreiben sollten, immer für uns vorbereitet und erledigt wird. Aus diesem Grund ist DI so eng mit Inversion of Control (IoC) verbunden, dass unser Programm die Steuerung eines anderen laufenden Moduls übergibt, das Beans in unseren Code injiziert. (Jedes Objekt, das injiziert werden kann, kann signiert oder als Bean betrachtet werden.) Im Frühjahr wird beispielsweise ApplicationContext erstellt und initialisiertContainer, der diese Arbeit für uns erledigt. Wir erstellen einfach in unserem Code den Kontext und rufen die Initialisierung der Beans auf. In diesem Moment wurde die Injektion automatisch durchgeführt.

Hariprasad
quelle
4

Abhängigkeitsinjektion für 5-Jährige.

Wenn Sie Dinge selbst aus dem Kühlschrank holen, können Sie Probleme verursachen. Sie könnten die Tür offen lassen, Sie könnten etwas bekommen, das Mama oder Papa nicht wollen, dass Sie haben. Möglicherweise suchen Sie sogar nach etwas, das wir nicht einmal haben oder das abgelaufen ist.

Was Sie tun sollten, ist ein Bedürfnis zu äußern: "Ich brauche etwas zum Mittagessen zu trinken", und dann werden wir sicherstellen, dass Sie etwas haben, wenn Sie sich zum Essen hinsetzen.

Nithin Prasad
quelle
1
Dies ist eindeutig die Antwort eines Elternteils. ;)
Marche Remi
4

Aus Christoffer Noring, Pablo Deelemans Buch „Learning Angular - Second Edition“:

„Da unsere Anwendungen wachsen und sich entwickelt, jeder unserer Code Einheiten werden intern benötigen Instanzen anderer Objekte , die besser als bekannt sind , sind Abhängigkeiten in der Welt der Software - Engineering. Die Aktion der Weitergabe solcher Abhängigkeiten zu den abhängigen Client ist bekannt als Injektion , und es beinhaltet auch die Teilnahme einer anderen Code-Entität, die als Injektor bezeichnet wird . Der Injektor übernimmt die Verantwortung für das Instanziieren und Bootstrapping des erforderlichen AbhängigkeitenSie sind also ab dem Moment einsatzbereit, in dem sie erfolgreich in den Client injiziert wurden. Dies ist sehr wichtig, da der Client nichts darüber weiß, wie er seine eigenen Abhängigkeiten instanziieren kann, und nur die Schnittstelle kennt, die er implementiert, um sie zu verwenden. "

Von: Anton Moiseev. Buch „Angular Development with Typescript, Second Edition.“:

„Kurz gesagt, DI hilft Ihnen , Code in einem schreiben lose gekoppelter Weise und macht Ihren Code mehr prüfbar und wiederverwendbar .“

HS Progr
quelle
3

Mit einfachen Worten: Abhängigkeitsinjektion (DI) ist der Weg, um Abhängigkeiten oder enge Kopplungen zwischen verschiedenen Objekten zu entfernen. Die Abhängigkeitsinjektion verleiht jedem Objekt ein zusammenhängendes Verhalten.

DI ist die Implementierung des IOC-Principals von Spring, das besagt: "Rufen Sie uns nicht an, wir rufen Sie an". Bei Verwendung des Abhängigkeitsinjektionsprogrammierers muss kein Objekt mit dem neuen Schlüsselwort erstellt werden.

Objekte werden einmal in den Spring-Container geladen und dann bei Bedarf wiederverwendet, indem wir diese Objekte mit der Methode getBean (String beanName) aus dem Spring-Container abrufen.

Waqas Ahmed
quelle
3

Die Abhängigkeitsinjektion ist das Herzstück des Konzepts im Zusammenhang mit Spring Framework. Während die Erstellung des Frameworks für jedes Projekt eine wichtige Rolle spielt, spielt die Abhängigkeitsinjektion hier eine wichtige Rolle.

Angenommen, Sie haben in Java zwei verschiedene Klassen als Klasse A und Klasse B erstellt und unabhängig davon, welche Funktionen in Klasse B verfügbar sind, die Sie in Klasse A verwenden möchten. Zu diesem Zeitpunkt kann also die Abhängigkeitsinjektion verwendet werden. Wenn Sie Objekte einer Klasse in einer anderen Klasse erstellen können, können Sie auf die gleiche Weise eine ganze Klasse in eine andere Klasse einfügen, um sie zugänglich zu machen. Auf diese Weise kann die Abhängigkeit überwunden werden.

ABHÄNGIGE INJEKTION VERKLEIDET EINFACH ZWEI KLASSEN UND HÄLT SIE GLEICHZEITIG.

Mohit Sarsar
quelle