Was ist die beste Definition für Abhängigkeitsinjektion?


10

Jedes Mal, wenn mich jemand erreicht und mich bittet, die Abhängigkeitsinjektion konzeptionell zu definieren und die tatsächlichen Vor- und Nachteile der Verwendung von DI im Software-Design zu erläutern. Ich gebe zu, dass ich einige Schwierigkeiten habe, die Konzepte von DI zu erklären. Jedes Mal, wenn ich ihnen die Geschichte über das Prinzip der Einzelverantwortung, die Zusammensetzung über die Vererbung usw. erzählen muss.

Kann mir jemand helfen, die beste Art zu erklären, DI für Entwickler zu beschreiben?


2
Die Herausforderung hierbei ist, dass es so viele widersprüchliche Definitionen von DI gibt. Ich nehme die "reine DI" -Stellung ein: Wenn ich eine Funktion habe, die sich auf ihre Parameter stützt, um alle Zustände, Daten usw. bereitzustellen, dann verwendet diese Funktion DI. Im anderen Extremfall werden einige argumentieren, dass es ohne ein DI-Framework keine Abhängigkeitsinjektion gibt (obwohl sie natürlich falsch sind;)). Wenn Sie also keine Definition festlegen können, können Sie nicht erklären, was es ist ...
David Arno

Soweit ich weiß, ist dies nicht nur ein Problem von mir.
Tiago Sampaio



Es kommt alles darauf an: Die Abhängigkeitsinjektion ist eine Technik, die verwendet wird, um eine Abhängigkeitsinversion zu erreichen; Alles andere ist nur zusätzliches Zeug, das darauf aufgebaut ist. Beachten Sie, dass in diesen beiden Begriffen das Wort "Abhängigkeit" leicht unterschiedliche Bedeutungen hat. Bei der Abhängigkeitsinjektion bezieht es sich auf die Komponente, von der der Code abhängig ist. Bei der Abhängigkeitsinversion bezieht es sich auf die (gerichtete) Beziehung selbst - diejenige, die wir invertieren möchten. Letzteres ist das Ziel, daher sind die wichtigsten Vor- und Nachteile gleich. plus einige zusätzliche Bedenken im Zusammenhang mit der tatsächlichen Implementierung, wie z. B. die Verwaltung der Objektlebensdauer.
Filip Milovanović

Antworten:


22

Dependency Injection ist ein schrecklicher Name (IMO) 1 für ein recht einfaches Konzept. Hier ist ein Beispiel:

  1. Sie haben eine Methode (oder Klasse mit Methoden), die X ausführt (z. B. Daten aus der Datenbank abrufen).
  2. Im Rahmen von X erstellt und verwaltet diese Methode eine interne Ressource (z DbContext. B. a ). Diese interne Ressource wird als Abhängigkeit bezeichnet
  3. Sie entfernen das Erstellen und Verwalten der Ressource (dh DbContext) aus der Methode und machen es zur Verantwortung des Aufrufers, diese Ressource bereitzustellen (als Methodenparameter oder nach Instanziierung der Klasse).
  4. Sie führen jetzt die Abhängigkeitsinjektion durch.


[1] : Ich komme aus einer niedrigeren Ebene und habe Monate gebraucht, um mich hinzusetzen und die Abhängigkeitsinjektion zu lernen, da der Name impliziert, dass es viel komplizierter wäre, wie DLL-Injektion . Die Tatsache, dass Visual Studio (und wir Entwickler im Allgemeinen) auf die .NET-Bibliotheken (DLLs oder Assemblys ) verweist, von denen ein Projekt als Abhängigkeiten abhängt, hilft überhaupt nicht. Es gibt sogar so etwas wie den Dependency Walker (abhängige.exe) .


[Bearbeiten] Ich dachte, ein Demo-Code würde für einige nützlich sein, also hier ist einer (in C #).

Ohne Abhängigkeitsinjektion:

public class Repository : IDisposable
{
    protected DbContext Context { get; }

    public Repository()
    {
        Context = new DbContext("name=MyEntities");
    }

    public void Dispose()
    {
        Context.Dispose();
    }
}

Ihr Verbraucher würde dann so etwas tun:

using ( var repository = new Repository() )
{
    // work
}

Dieselbe Klasse, die mit einem Abhängigkeitsinjektionsmuster implementiert wurde, würde folgendermaßen aussehen:

public class RepositoryWithDI
{
    protected DbContext Context { get; }

    public RepositoryWithDI(DbContext context)
    {
        Context = context;
    }
}

Es liegt nun in der Verantwortung des Anrufers, a zu instanziieren DbContextund an Ihre Klasse weiterzuleiten (errm, injizieren ):

using ( var context = new DbContext("name=MyEntities") )
{
    var repository = new RepositoryWithDI(context);

    // work
}

3
Dies sollte zu Wikipedia hinzugefügt werden.
Evorlor

2
Es liegt nun in der Verantwortung des Anrufers, einen DbContext zu instanziieren. Ich denke, dies liegt in der Verantwortung des Einstiegspunkts der Anwendung, alle erforderlichen Abhängigkeiten zu instanziieren. Der Verbraucher muss also nur den erforderlichen Typ als Abhängigkeit in den eigenen Vertrag einführen.
Fabio

@ Fabio Es könnte sein. (In diesem Fall ist der Aufrufer dafür verantwortlich, die beim Start der Anwendung instanziierte Ressource für die aufgerufene Methode / Klasse bereitzustellen.) In meinem Beispiel ist dies jedoch nicht der Fall, da dies keine Voraussetzung für die Erläuterung des Konzepts der Abhängigkeitsinjektion ist .
23. März 77,

5

Abstrakte Konzepte lassen sich oft besser anhand einer realen Analogie erklären. Dies ist meine Analogie:

Sie betreiben einen Sandwichladen. Sie machen erstaunliche Sandwiches, aber Sie wissen wenig bis gar nichts über Brot selbst. Sie haben nur langweiliges Weißbrot. Ihr Job konzentriert sich ganz auf die Beläge, mit denen Sie das Brot in ein Sandwich verwandeln.

Einige Ihrer Kunden würden jedoch Schwarzbrot wirklich bevorzugen. Einige würden Vollkorn bevorzugen. Es ist dir auch egal, du kannst jedes tolle Sandwich machen, solange es ein Brot von ähnlicher Größe ist. Sie möchten auch wirklich nicht die zusätzliche Verantwortung übernehmen müssen, verschiedene Brotsorten zu beschaffen und die Lagerbestände aufrechtzuerhalten. Selbst wenn Sie mehrere Brotsorten auf Lager haben, wird es immer einen Kunden mit einem exotischen Geschmack im Brot geben, den Sie vernünftigerweise nicht vorhersehen konnten.

Sie führen also eine neue Regel ein: Kunden bringen ihr eigenes Brot mit. Sie stellen selbst kein Brot mehr zur Verfügung. Dies ist eine Win-Win-Situation: Kunden erhalten genau das Brot, das sie möchten, und Sie müssen sich nicht mehr um die Beschaffung des Brotes kümmern, das Ihnen egal ist. Immerhin bist du ein Sandwichmaker, kein Bäcker.

Oh, und um den Kunden gerecht zu werden, die kein eigenes Brot kaufen möchten, eröffnen Sie nebenan einen zweiten Laden, in dem Sie Ihre originalen milden Weißbrote verkaufen. Kunden, die kein eigenes Brot mitgebracht haben, müssen nur das Standardbrot besorgen und dann zu Ihnen kommen, um ein Sandwich damit zu machen.

Es ist nicht perfekt, aber es unterstreicht das Hauptmerkmal: dem Verbraucher die Kontrolle zu geben . Die inhärente Win-Win-Situation besteht darin, dass Sie keine eigenen Abhängigkeiten mehr erwerben müssen und Ihr Verbraucher bei der Auswahl der Abhängigkeit ungehindert ist.


1
Ich mag das , aber die OP ist Suche nach einer Erklärung für Entwickler . Eine anfängliche Abstraktion ist gut, aber früher oder später würde sie ein reales Beispiel brauchen.
Robbie Dee

1
@RobbieDee: Wenn der Zweck des Musters klar ist, wird auch seine Implementierung klar. Zum Beispiel ist Marc's Antwort absolut richtig, aber ich habe das Gefühl, dass die Erklärung durch die Komplexität der von ihm verwendeten Beispielsituation ins Stocken gerät. Dies läuft darauf hinaus, "Wenn Sie ein Schiff bauen wollen, trommeln Sie keine Menschen auf, um Holz zu sammeln, und weisen Sie ihnen keine Aufgaben und Arbeiten zu, sondern bringen Sie ihnen bei, sich nach der endlosen Unermesslichkeit des Meeres zu sehnen." . Anstatt zu erklären, was zu tun ist, erkläre ich lieber, warum es zu tun ist.
Flater

2
Sie haben natürlich Recht, aber ich kann nicht anders, als zu glauben, ich brauche ein konkretes Beispiel - zum Beispiel, dass ich kein echtes Dateisystem oder keine Datenbank habe, um Appetit zu machen, aber vielleicht ist das nur meine enge Entwickleransicht :)
Robbie Dee

1

Einfache Antwort darauf:

Zuallererst sollte eine Klasse eine klar definierte Verantwortung haben, und alles außerhalb dieses Bereichs sollte außerhalb dieser Klasse aufbewahrt werden. Vor diesem Hintergrund bedeutet Abhängigkeitsinjektion, dass Sie eine Funktion aus einer anderen Klasse B mithilfe der Hilfe eines "Drittanbieters" in eine Klasse A einfügen, um diese Trennung von Bedenken zu erreichen, und Klasse A dabei helfen, einen Vorgang abzuschließen, der außerhalb ihres Geltungsbereichs liegt.

.Net Core ist ein ziemlich gutes Beispiel, das Sie geben können, da dieses Framework viel Abhängigkeitsinjektion verwendet. Im Allgemeinen befinden sich die Dienste, die Sie injizieren möchten, in der startup.csDatei.

Sicher, der Schüler sollte einige Konzepte wie Polymorphismus, Schnittstellen und OOP-Design-Prinzipien kennen.


0

Es gibt viel Flaum und Bunker um das, was im Wesentlichen ein einfaches Konzept ist.

Es ist auch sehr leicht, sich mit " Welches Framework sollte ich verwenden " festzumachen, wenn Sie es ganz einfach in Code tun können.

Dies ist die Definition, die ich persönlich verwende:

Bei gegebenem Verhalten X mit einer Abhängigkeit von Y. Bei der Abhängigkeitsinjektion kann jedes Y bereitgestellt werden, das die Kriterien für eine Instanz von Y erfüllt, auch wenn Sie keine haben.

Einige Beispiele könnten sein, wenn Y ein Dateisystem oder eine Datenbankverbindung ist.

Frameworks wie moq ermöglichen die Definition von Doubles ( Pretend- Versionen von Y) über eine Schnittstelle, sodass eine Instanz von Y eingefügt werden kann , wobei Y beispielsweise eine Datenbankverbindung ist.

Es ist leicht, in die Falle zu tappen, zu glauben, dass dies nur ein Unit-Testing-Problem ist, aber es ist ein sehr nützliches Muster für jeden Code, bei dem Änderungen erwartet werden, und es ist wahrscheinlich trotzdem eine gute Praxis.


0

Wir stellen das Verhalten einer Funktion zur Laufzeit durch die Methode zum Einfügen dieses Verhaltens in die Funktion über einen Parameter bereit.

Das Strategiemuster ist ein hervorragendes Beispiel für die Abhängigkeitsinjektion.


0

Um dies richtig zu machen, müssen wir zuerst Abhängigkeiten und Injektionen definieren.

  • Abhängigkeit: Jede Ressource, die eine Operation benötigt.
  • Injektion: Übergabe dieser Ressource an die Operation, normalerweise als Argument an eine Methode.

Ein rudimentäres Beispiel wäre eine Methode, die zwei Werte hinzufügt. Offensichtlich müssen bei dieser Methode die Werte hinzugefügt werden. Wenn sie durch Übergabe als Argumente bereitgestellt werden, handelt es sich bereits um eine Abhängigkeitsinjektion. Die Alternative wäre, die Operanden als Eigenschaften oder globale Variablen zu implementieren. Auf diese Weise würden keine Abhängigkeiten eingefügt, die Abhängigkeiten wären im Voraus extern verfügbar.

Angenommen, Sie verwenden stattdessen Eigenschaften und benennen sie A und B. Wenn Sie die Namen in Op1 und Op2 ändern, wird die Add-Methode unterbrochen. Oder Ihre IDE würde alle Namen für Sie aktualisieren. Der Punkt ist, dass die Methode ebenfalls aktualisiert werden muss, da sie von externen Ressourcen abhängig ist.

Dieses Beispiel ist einfach, aber Sie können sich komplexere Beispiele vorstellen, bei denen die Methode eine Operation an einem Objekt wie einem Bild ausführt oder aus einem Dateistream liest. Möchten Sie, dass die Methode nach dem Bild greift und wissen muss, wo es sich befindet? Nein. Möchten Sie, dass die Methode die Datei selbst öffnet und weiß, wo sie nach der Datei suchen muss oder ob sie aus einer Datei liest? Nein.

Der Punkt: Die Funktionalität einer Methode auf ihr Kernverhalten reduzieren und die Methode von ihrer Umgebung entkoppeln. Sie erhalten das erste, indem Sie das zweite ausführen. Sie können dies als Definition der Abhängigkeitsinjektion betrachten.

Die Vorteile: Da Abhängigkeiten für die Umgebung der Methode beseitigt wurden, wirken sich Änderungen an der Methode nicht auf die Umgebung aus und umgekehrt. => Die Anwendung wird einfacher zu warten (zu ändern).

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.