design patterns - pattern - Was ist Abhängigkeitsinjektion?




pattern ioc (20)

Was ist Abhängigkeitsinjektion?

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

So funktioniert Dependency Injection im Frühling:

Wir müssen das Objekt nicht mit einem neuen Schlüsselwort hart codieren, sondern 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. Abhängigkeitseinspritzung ist ein konkretes Beispiel für IOC.

Zwei Arten von Abhängigkeitseinspritzung:

  1. Konstruktorinjektion
  2. Setter-Injektion

1. Konstruktor-basierte Abhängigkeitsinjektion:

Konstruktor-basierte DI wird ausgeführt, 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-based 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 Argument aufgerufen wurde, um Ihr Bean zu instantiieren.

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"/>

HINWEIS: Es ist eine gute Faustregel, Konstruktorargumente für zwingende Abhängigkeiten und Setter für optionale Abhängigkeiten zu verwenden. Beachten Sie, dass die Annotation, die als @Required-Annotation für einen Setter verwendet wird, verwendet werden kann, um Setter als erforderliche Abhängigkeiten zu erstellen.

Es wurden bereits mehrere Fragen mit spezifischen Fragen zur Abhängigkeitsinjektion gepostet, z. B. wann sie verwendet werden sollen und welche Rahmenbedingungen dafür zur Verfügung stehen. Jedoch,

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


Abhängigkeitsinjektion ist eine Praxis, bei der Objekte so entworfen werden, dass sie Instanzen der Objekte von anderen Codeteilen erhalten, anstatt sie intern zu erstellen. Dies bedeutet, dass jedes Objekt, das die Schnittstelle implementiert, das von dem Objekt benötigt wird, ohne Änderung des Codes ersetzt werden kann, 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::addManager und PersonService::removeManager eine Instanz des GroupMembershipService erfordern, um seine Arbeit ausführen zu können. Ohne Dependency Injection besteht die herkömmliche Methode darin, einen neuen GroupMembershipService im Konstruktor von PersonService zu instanziieren und dieses PersonService in beiden Funktionen zu verwenden. Wenn der Konstruktor von GroupMembershipService jedoch mehrere Dinge benötigt oder, was noch schlimmer ist, gibt es einige Initialisierungs-Setter, die für GroupMembershipService aufgerufen werden GroupMembershipService . Der Code wächst ziemlich schnell, und der PersonService hängt jetzt nicht nur von GroupMembershipService sondern ab auch alles andere, von dem GroupMembershipService abhängt. Darüber hinaus ist die Verknüpfung mit GroupMembershipService in den PersonService bedeutet, dass Sie einen GroupMembershipService zu Testzwecken "dummy-up" GroupMembershipService oder ein Strategiemuster in verschiedenen Teilen Ihrer Anwendung verwenden können.

Mit Dependency Injection können Sie den GroupMembershipService in Ihrem PersonService instanziieren, sondern entweder an den PersonService Konstruktor übergeben oder eine Property (Getter und Setter) hinzufügen, um eine lokale Instanz davon PersonService . Das bedeutet, dass sich Ihr PersonService nicht länger um die Erstellung eines GroupMembershipService kümmern GroupMembershipService , er akzeptiert nur die angegebenen GroupMembershipService und arbeitet mit ihnen. Dies bedeutet auch, dass alles, was eine Unterklasse von GroupMembershipService ist oder die GroupMembershipService Schnittstelle implementiert, in den GroupMembershipService "injiziert" werden PersonService , und der PersonService muss nicht über die Änderung Bescheid wissen.


Bedeutet "Abhängigkeitseinspritzung" nicht nur parametrisierte Konstruktoren und öffentliche 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(); 
    ... 
  } 
}

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

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

DI ist in der Welt der objektorientierten Programmierung analog. Die Werte anstelle von konstanten Literalen sind ganze Objekte. Der Grund für das Verschieben des Codes aus dem Klassencode ist jedoch ä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.


Lassen Sie uns ein einfaches Beispiel mit Auto- und Motorklassen versuchen. Jedes Auto braucht einen Motor, um zumindest irgendwohin zu gehen. Im Folgenden wird beschrieben, wie Code ohne Abhängigkeitseingabe 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!");
    }
}

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

Car car = new Car();

Das Problem mit diesem Code, dass wir eng mit GasEngine gekoppelt sind, und wenn wir uns entscheiden, es in ElectricityEngine zu ändern, müssen wir die Klasse Car neu schreiben. Und je größer die Anwendung ist, desto mehr Probleme und Kopfschmerzen müssen wir hinzufügen und verwenden, um einen neuen Motortyp zu verwenden.

Mit anderen Worten, bei diesem Ansatz ist unsere High-Level-Klasse Car von der Klasse GasEngine der unteren Ebene abhängig, die gegen das Abhängigkeitsumkehrprinzip (DIP) von SOLID verstößt. DIP legt nahe, dass wir uns auf Abstraktionen verlassen sollten, nicht auf konkrete Klassen. Um dies zu befriedigen, führen wir die IEngine-Schnittstelle ein und schreiben den Code wie folgt um:

    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 Car-Klasse nur von der IEngine-Schnittstelle abhängig, nicht von einer bestimmten Implementierung der Engine. Der einzige Trick besteht nun darin, wie wir eine Instanz des Autos erstellen und ihm eine konkrete konkrete Motorklasse wie GasEngine oder ElectricityEngine zuweisen. Hier kommt die Abhängigkeitsinjektion ins Spiel .

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

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

Der Hauptvorteil von Dependency Injection besteht darin, dass Klassen locker gekoppelt sind, da sie keine fest codierten Abhängigkeiten haben. Dies folgt dem Prinzip der Inversion der Abhängigkeit, das oben erwähnt wurde. 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 nur eine Technik, um eine lockere Kopplung zwischen Objekten und ihren Abhängigkeiten zu erreichen. Anstatt die Abhängigkeiten, die die Klasse zum Ausführen ihrer Aktionen benötigt, direkt zu instanziieren, werden Abhängigkeiten für die Klasse (meistens) über die Konstruktorinjektion bereitgestellt.

Wenn wir viele Abhängigkeiten haben, ist es sehr empfehlenswert, Inversion of Control (IoC) -Container zu verwenden, um festzustellen, welche Schnittstellen für alle unsere Abhängigkeiten den konkreten Implementierungen zugeordnet werden sollten, und wir können diese Abhängigkeiten für uns auflösen, wenn sie erstellt werden unser Objekt Zum Beispiel könnten wir im Mapping 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, erstellt er automatisch unsere Car- Klasse mit einer GasEngine- Abhängigkeit übergeben.

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

Abhängigkeitsinjektion ist ein Muster, das es Ihrer Anwendung ermöglicht, Objekte spontan in Klassen einzufügen, die sie benötigen, ohne dass diese Klassen für diese Objekte verantwortlich sind. Dadurch kann Ihr Code locker gekoppelt werden, und Entity Framework Core kann in dasselbe Servicesystem eingefügt werden.


Stellen wir uns vor, Sie wollen angeln gehen:

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

  • Mit der Abhängigkeitseinspritzung kümmert sich ein anderer um die Vorbereitung und stellt Ihnen die erforderliche Ausrüstung zur Verfügung. Sie werden das Boot, die Angelrute und den Köder erhalten ("injiziert") - alles einsatzbereit.


Abhängigkeitsinjektion bedeutet einen Weg (tatsächlich auf irgendeine Weise ) für einen Teil des Codes (z. B. eine Klasse), um auf Abhängigkeiten (andere Teile des Codes, z. B. andere Klassen, auf die er angewiesen ist) modular zuzugreifen, ohne dass diese fest codiert sind (also Sie können sich ändern oder überschreiben oder je nach Bedarf zu einem anderen Zeitpunkt geladen werden.

(und ps, ja, es ist zu einem übermäßig gehypten Namen für ein ziemlich einfaches Konzept geworden) , meine .25Cents


Ich fand dieses lustige Beispiel in Bezug auf lose Kopplung :

Jede Anwendung besteht aus vielen Objekten, die zusammenarbeiten, um nützliche Funktionen 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 beispielsweise ein Car Objekt.

Ein Car hängt von Rädern, Motor, Kraftstoff, Batterie usw. ab. Traditionell definieren wir die Marke solcher abhängiger Objekte zusammen mit der Definition des Car Objekts.

Ohne Abhängigkeitseinspritzung (DI):

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

  //The rest
}

Hier ist das Car Objekt für das Erstellen der abhängigen Objekte verantwortlich.

Was ist, wenn wir den Typ des abhängigen Objekts ändern möchten - sagen Sie Wheel -, nachdem die ersten NepaliRubberWheel() Punktionen NepaliRubberWheel() ? Wir müssen das Car-Objekt mit seiner neuen Abhängigkeit, beispielsweise ChineseRubberWheel() , neu ChineseRubberWheel() , dies kann jedoch nur der Autohersteller.

Was bringt uns dann die Dependency Injection ...?

Bei Verwendung der Abhängigkeitseinspritzung erhalten Objekte ihre Abhängigkeit zur Laufzeit und nicht zur Kompilierzeit (Fahrzeugherstellungszeit) . Damit wir das Wheel jetzt wechseln können, wann immer wir wollen. Hier kann die dependency ( wheel ) zur Laufzeit in Car eingeblendet werden.

Nach 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


Alle obigen Antworten sind gut, mein Ziel ist es, das Konzept auf einfache Weise zu erklären, so dass jeder, der keine Programmierkenntnisse hat, das Konzept auch verstehen kann

Abhängigkeitsinjektion ist eines der Entwurfsmuster, das uns dabei hilft, komplexe Systeme auf einfachere Weise zu erstellen.

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

Das obige Bild ist ein Bild eines tragbaren Tonbandgeräts mit Bandrolle aus der Mitte des 20. Jahrhunderts. Source .

Die Hauptaufgabe eines Tonbandgerätes besteht darin, Ton aufzunehmen oder wiederzugeben.

Beim Entwerfen eines Systems ist eine Spule 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, wo 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 den zweiten entscheiden, also einen Haken für die Rolle, legen wir den zusätzlichen Vorteil an, Musik zu spielen, indem wir die Rolle wechseln. und reduziert die Funktion nur auf das Spielen auf der Rolle.

Ebenso wie Abhängigkeitsinjektion ist der Vorgang des Externalisierens 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 wichtigsten Vorteile, die wir durch die Verwendung der Abhängigkeitseinspritzung erzielt haben.

  • Hohe Kohäsion und lose Kopplung.
  • Abhängigkeit veräußern und nur auf Verantwortung achten.
  • Dinge zu Komponenten machen und zu großen Systemen mit hohen Fähigkeiten kombinieren.
  • Es hilft, qualitativ hochwertige Komponenten zu entwickeln, da sie unabhängig voneinander entwickelt und ordnungsgemäß getestet werden.
  • Es hilft, die Komponente durch eine andere zu ersetzen, wenn eine ausfällt.

Heute ist dieses Konzept die Basis für bekannte Rahmen in der Programmierwelt. Spring Angular usw. sind die bekannten Software-Frameworks, die auf diesem Konzept basieren

Abhängigkeitseinspritzung ist ein Muster, das zum Erstellen von Instanzen von Objekten verwendet wird, auf die andere Objekte angewiesen sind, ohne zur Kompilierzeit zu wissen, welche Klasse zum Bereitstellen dieser Funktionalität verwendet wird, oder einfach die Art des Injizierens von Eigenschaften in ein Objekt als Abhängigkeitsinjektion bezeichnet wird.

Beispiel für Abhängigkeitsinjektion

Bisher schreiben wir Code so

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ängigkeitseinspritzung nimmt der Abhängigkeitsinjektor die Instantiierung 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


Was ist Abhängigkeitsinjektion (DI)?

Wie andere bereits gesagt haben, entfernt Dependency Injection (DI) die Verantwortung für die direkte Erstellung und Verwaltung der Lebensdauer anderer Objektinstanzen, auf die unsere Interessensklasse (Konsumentenklasse) (im Sinne der UML ) angewiesen ist. Diese Instanzen werden stattdessen an unsere Konsumentenklasse übergeben, normalerweise als Konstruktorparameter oder über Eigenschaftensetzer (die Verwaltung der Instanzierung des Abhängigkeitsobjekts und die Weitergabe an die Konsumerklasse wird normalerweise von einem Inversion of Control- Container (IoC- Container ) durchgeführt. Dies ist jedoch ein anderes Thema.) .

DI, DIP und SOLID

Im Paradigma der SOLID-Prinzipien von Robert C Martin für objektorientiertes Design ist DI eine der möglichen Implementierungen des Abhängigkeitsinversion-Prinzips (DIP) . Das DIP ist das D des SOLID Mantras - andere DIP-Implementierungen umfassen den Service Locator und die Plugin-Muster.

Das Ziel des DIP besteht darin, enge, konkrete Abhängigkeiten zwischen Klassen zu entkoppeln und stattdessen die Kopplung mittels einer Abstraktion zu lösen, die je nach Sprache und Ansatz über eine interface , eine abstract class oder eine pure virtual class erreicht werden kann .

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

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

Nach der Anwendung des DIP wird das Erfordernis gelockert und das Anliegen, die Lebensdauer der Foo Abhängigkeit zu erhalten und zu verwalten, beseitigt:

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

Warum DIP (und DI) verwenden?

Durch die Entkopplung von Abhängigkeiten zwischen Klassen auf diese Weise können diese Abhängigkeitsklassen einfach durch andere Implementierungen ersetzt werden, die auch die Voraussetzungen der Abstraktion erfüllen (z. B. kann die Abhängigkeit mit einer anderen Implementierung derselben Schnittstelle umgeschaltet werden). Wie andere bereits erwähnt haben, besteht der häufigste Grund für die Entkopplung von Klassen über das DIP darin, dass eine verbrauchende Klasse isoliert getestet werden kann, da dieselben Abhängigkeiten jetzt unterbunden und / oder simuliert werden können.

Eine Folge von DI ist, dass die Verwaltung der Lebensdauer 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-Injektion).

Dies kann auf verschiedene Arten angezeigt werden:

  • Wenn die Lebensdauerkontrolle der Abhängigkeiten durch die konsumierende Klasse beibehalten werden muss, kann die Kontrolle wiederhergestellt werden, indem eine (abstrakte) Factory zum Erstellen der Instanzen der Abhängigkeitsklasse in die Konsumerklasse eingefügt wird. Der Verbraucher kann Instanzen nach Bedarf über ein Create im Werk abrufen und diese Instanzen entsorgen, sobald sie abgeschlossen sind.
  • Oder die Lebensdauerkontrolle von Abhängigkeitsinstanzen kann einem IoC-Container übergeben werden (mehr dazu unten).

Wann DI verwenden?

  • Wenn es wahrscheinlich ist, dass eine gleichwertige Implementierung durch eine Abhängigkeit ersetzt wird,
  • Wann immer Sie die Methoden einer Klasse unabhängig von ihren Abhängigkeiten testen müssen,
  • Wo Ungewissheit über die Lebensdauer einer Abhängigkeit ein Experiment rechtfertigen kann (z. B. Hey, MyDepClass ist Thread-sicher - was ist, wenn wir es zu einem MyDepClass machen und dieselbe Instanz in alle Konsumenten MyDepClass ?)

Beispiel

Hier ist eine einfache C # -Implementierung. In Anbetracht der folgenden Verbrauchsklasse:

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 static Abhängigkeiten von zwei anderen Klassen, System.DateTime und System.Console , die nicht nur die Protokollierungsausgabeoptionen einschränken (das Protokollieren auf der Konsole ist wertlos, wenn niemand zuschaut), aber noch schwieriger ist es um die Abhängigkeit von einer nicht deterministischen Systemuhr automatisch zu testen.

Wir können jedoch DIP auf diese Klasse anwenden, indem wir das Anliegen des Zeitstempels als Abhängigkeit ausschließen und MyLogger nur an eine einfache Schnittstelle MyLogger :

public interface IClock
{
    DateTime Now { get; }
}

Wir können die Abhängigkeit von Console auf eine Abstraktion wie einen TextWriter . Dependency Injection wird normalerweise als constructor (Übergabe einer Abstraktion an eine Abhängigkeit als Parameter an den Konstruktor einer konsumierenden Klasse) oder Setter Injection (Weitergabe der Abhängigkeit über einen setXyz() Setter oder eine .Net-Eigenschaft mit {set;} definiert). Konstruktorinjektion wird bevorzugt, da dies garantiert, dass sich die Klasse nach dem Konstruieren in einem korrekten Status befindet und die internen Abhängigkeitsfelder als readonly (C #) oder als final (Java) gekennzeichnet werden können. Wenn Sie also die Konstruktorinjektion für das obige Beispiel verwenden, bleiben uns folgende Möglichkeiten:

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 muss eine konkrete Clock werden, die natürlich zu DateTime.Now zurückkehren DateTime.Now , und die beiden Abhängigkeiten müssen von einem IoC-Container über die Konstruktorinjektion bereitgestellt werden.)

Es kann ein automatisierter Komponententest erstellt werden, der definitiv beweist, dass unser Logger ordnungsgemäß funktioniert, da wir jetzt die Abhängigkeiten kontrollieren - die Zeit und die geschriebene 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

Abhängigkeitseinspritzung ist immer mit einem Inversion of Control-Container (IoC) verbunden , um die konkreten Abhängigkeitsinstanzen einzuspeisen (bereitzustellen) und Lebensdauerinstanzen zu verwalten. Während des Konfigurations- / Bootstrapping-Prozesses lassen sich mit IoC Containern Folgendes definieren:

  • Abbildung zwischen jeder Abstraktion und der konfigurierten konkreten Implementierung (z. B. " IBar immer ein Verbraucher eine IBar , eine ConcreteBar Instanz zurückgeben" )
  • Es können Richtlinien für die Verwaltung der Lebensdauer jeder Abhängigkeit eingerichtet werden, z. B. um ein neues Objekt für jede Verbraucherinstanz zu erstellen, um eine Einzelabhängigkeitsbeziehungsinstanz für alle Verbraucher gemeinsam zu nutzen, um dieselbe Abhängigkeitsinstanz nur über denselben Thread zu teilen usw.
  • In .Net kennen IoC-Container Protokolle wie IDisposable und übernehmen die Verantwortung für das Disposing Abhängigkeiten im Einklang mit dem konfigurierten Lebenszyklusmanagement.

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

Der Schlüssel zu DI-freundlichem Code besteht darin, die statische Kopplung von Klassen zu vermeiden und new () nicht für die Erstellung von Abhängigkeiten zu verwenden

Wie im obigen Beispiel erfordert die Entkopplung von Abhängigkeiten einige Entwicklungsanstrengungen, und für den Entwickler ist ein Paradigmenwechsel erforderlich, um die Angewohnheit zu brechen, newAbhängigkeiten direkt zu brechen und stattdessen dem Container zu vertrauen, um Abhängigkeiten zu verwalten.

Die Vorteile sind jedoch zahlreich, insbesondere in der Möglichkeit, Ihre Interessensgruppe gründlich zu testen.

Hinweis : Die Erstellung / Abbildung / Projektion (über new ..()) von POCO / POJO / Serialisierungs-DTOs / Entity-Diagrammen / anonymen JSON-Projektionen usw. - dh "Nur Daten" -Klassen oder -Datensätze, die von Methoden verwendet oder zurückgegeben werden, werden nicht als Abhängigkeiten betrachtet UML-Sinn) und nicht DI. Mit newdiesen zu projizieren ist gut so .


Ich denke, da jeder für DI geschrieben hat, lassen Sie mich ein paar Fragen stellen.

  1. Wenn Sie eine Konfiguration von DI haben, bei der alle tatsächlichen Implementierungen (keine Schnittstellen), die in eine Klasse eingefügt werden sollen (z. B. Dienste für einen Controller), warum handelt es sich dann nicht um eine Art Hardcoding?
  2. Was ist, wenn ich das Objekt zur Laufzeit ändern möchte? Zum Beispiel sagt meine Konfiguration bereits, wenn ich MyController instanziiere, für FileLogger als ILogger einspritze. Aber ich möchte DatabaseLogger injizieren.
  3. Jedes Mal, wenn ich ändern möchte, welche Objekte meine AClass benötigt, muss ich jetzt an zwei Stellen nachsehen - in die Klasse selbst und in die Konfigurationsdatei. Wie macht das das Leben leichter?
  4. Wenn Aproperty of AClass nicht eingespritzt wird, ist es schwieriger, es zu verspotten?
  5. Zurück zur ersten Frage. Wenn die Verwendung des neuen Objekts () schlecht ist, wie wird die Implementierung und nicht die Schnittstelle eingefügt? Ich denke, viele von Ihnen sagen, dass wir die Schnittstelle tatsächlich einschleusen, aber durch die Konfiguration müssen Sie die Implementierung dieser Schnittstelle angeben. Nicht zur Laufzeit.

Dies basiert auf der Antwort @Adam N gepostet.

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 ist, haben Sie es als Eigenschaft. Sie können das ausspielen, unabhängig davon, ob Sie es injiziert haben oder nicht. Ich möchte nur, dass es injiziert wird, wenn GMService spezifischere untergeordnete Klassen hätte, die Sie bis zur Laufzeit nicht kennen würden. Dann möchten Sie die Unterklasse einfügen. Oder wenn Sie das als Singleton oder Prototyp verwenden wollten. Um ehrlich zu sein, enthält die Konfigurationsdatei alles, was für eine Unterklasse für einen Typ (Schnittstelle) während der Kompilierzeit eingefügt wird.

BEARBEITEN

Ein schöner Kommentar von Jose Maria Arranz zu DI

DI erhöht die Kohäsion, indem die Notwendigkeit, die Richtung der Abhängigkeit zu bestimmen und jeglichen Klebercode zu schreiben, aufgehoben wird.

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

DI reduziert die Kopplung, indem alle Ihre Komponenten modular (dh austauschbar) gemacht werden und über gut definierte Schnittstellen zueinander verfügen.

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

Über austauschbar: Mit einem sehr einfachen .properties-Archiv und Class.forName können Sie festlegen, welche Klassen geändert werden können. Wenn JEDE Klasse Ihres Codes geändert werden kann, ist Java nicht für Sie. Verwenden Sie eine Skriptsprache. Übrigens: Anmerkungen können nicht ohne Neukompilierung geändert werden.

Meiner Meinung nach gibt es einen einzigen Grund für DI-Gerüste: die Reduzierung der Kesselplatte. Mit einem durchdachten Factory-System können Sie das gleiche, besser kontrollierte und vorhersehbare System als Ihr bevorzugtes DI-Framework verwenden. DI-Frameworks versprechen Code-Reduktion (XML und Annotationen sind ebenfalls Quellcode). Das Problem ist, dass diese Boiler-Plattenreduzierung in sehr einfachen Fällen nur real ist (eine Instanz pro Klasse und ähnliches). Manchmal ist die Auswahl des geeigneten Service-Objekts in der Realität nicht so einfach wie das Zuordnen einer Klasse zu einem Einzelobjekt.


Abhängigkeitseinspritzung ist eine mögliche Lösung für das, was allgemein als "Abhängigkeitsverschleierung" bezeichnet werden könnte. Abhängigkeitsverschleierung ist eine Methode, um die "offensichtliche" Natur aus dem Prozess der Bereitstellung einer Abhängigkeit für eine Klasse, die diese erfordert, herauszunehmen und daher die Bereitstellung dieser Abhängigkeit für die Klasse in gewisser Weise zu verschleiern. Das ist nicht unbedingt eine schlechte Sache. Durch Verschleiern der Art und Weise, wie eine Abhängigkeit einer Klasse bereitgestellt wird, ist etwas außerhalb der Klasse für die Erzeugung der Abhängigkeit verantwortlich, was bedeutet, dass in verschiedenen Szenarien eine unterschiedliche Implementierung der Abhängigkeit ohne Änderungen an die Klasse geliefert werden kann zur Klasse. Dies ist ideal für den Wechsel zwischen Produktions- und Testmodus (z. B. Verwendung einer 'Mock'-Dienstabhängigkeit).

Leider ist das Schlimme, dass einige Leute davon ausgegangen sind, dass Sie ein spezielles Framework zur Abhängigkeitsverschleierung benötigen und dass Sie irgendwie ein "kleinerer" Programmierer sind, wenn Sie sich dafür entscheiden, kein bestimmtes Framework zu verwenden. Ein anderer, äußerst verstörender Mythos, von dem viele glauben, ist, dass Abhängigkeitsinjektion der einzige Weg ist, um Abhängigkeitsverschleierung 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 Abhängigkeitsverschleierungsanforderungen gibt.

Programmierer haben die Abhängigkeitsverschleierungsanforderung seit Jahren verstanden, und viele alternative Lösungen haben sich sowohl vor als auch nach dem Einsetzen der Abhängigkeitsinjektion entwickelt. Es gibt Factory-Muster, aber es gibt auch viele Möglichkeiten, ThreadLocal zu verwenden, bei denen keine Injektion in eine bestimmte Instanz erforderlich ist. Die Abhängigkeit wird effektiv in den Thread eingefügt, was den Vorteil hat, dass das Objekt (über statische Getter-Methoden) für alle verfügbar istKlasse, für die es erforderlich ist, ohne dass Anmerkungen zu den Klassen hinzugefügt werden müssen, die dies erfordern, und Sie müssen komplizierte XML-Elemente einrichten, 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 "transparente Persistenz" viel einfacher und mit Domänen- und Geschäftsmodellklassen erreichen, die ausschließlich aus POJOs bestehen (dh keine Framework-spezifischen / gesperrten Anmerkungen).



Das bedeutet, dass Objekte nur so viele Abhängigkeiten haben sollten, wie sie für ihre Arbeit benötigen, und dass die Abhängigkeiten gering sein sollten. Des Weiteren sollten die Abhängigkeiten eines Objekts auf Schnittstellen und nicht auf "konkrete" Objekte sein, wenn dies möglich ist. (Ein konkretes Objekt ist jedes Objekt, das mit dem Schlüsselwort new erstellt wurde.) Eine lose Kopplung fördert die Wiederverwendbarkeit, die Wartungsfreundlichkeit und die einfache Bereitstellung von „Scheinobjekten“ anstelle von teuren Diensten.

Die "Abhängigkeitsinjektion" (DI) wird auch als "Inversion of Control" (IoC) bezeichnet und kann als Technik zur Förderung dieser lockeren 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 den 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 den Wert null hat. Dies unterstreicht die Wichtigkeit, eine gültige Abhängigkeit zu erhalten. Constructor Injection 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

Beachten Sie jedoch folgendes Beispiel: Angenommen, Sie haben eine Klasse mit zehn Methoden, die keine Abhängigkeiten haben, aber Sie fügen eine neue Methode hinzu, die von IDAO abhängig ist. Sie können den Konstruktor so ändern, dass er 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, aber dann weiß ein Entwickler leicht, wann er einen Konstruktor über dem anderen verwenden soll. Wenn die Abhängigkeit sehr teuer ist, warum sollte sie erstellt und an den Konstruktor übergeben werden, wenn sie nur selten verwendet wird? "Setter Injection" ist eine weitere DI-Technik, die in solchen Situationen eingesetzt werden kann.

Setter Injection erzwingt keine Weitergabe von Abhängigkeiten an den Konstruktor. Stattdessen werden die Abhängigkeiten auf öffentliche Eigenschaften festgelegt, die von dem betreffenden Objekt verfügbar gemacht werden. Wie zuvor angedeutet, sind die Hauptmotivatoren dafür:

  1. Unterstützung der Abhängigkeitsinjektion, ohne den Konstruktor einer älteren Klasse ändern zu müssen.
  2. So können 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;

Der Sinn von Dependency Injection (DI) ist es, den Quellcode der Anwendung sauber und stabil zu halten :

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

In der Praxis trennt jedes Entwurfsmuster die Bedenken, damit zukünftige Änderungen die Mindestanzahl von Dateien beeinflussen.

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

Beispiel: DI mit Shellskript

Wenn Sie gelegentlich außerhalb von Java arbeiten, erinnern Sie sich daran, wie sourceoft in vielen Skriptsprachen (Shell, Tcl usw. oder sogar importin Python zu diesem Zweck missbraucht) 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 nicht erfolgreich alleine 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 "[email protected]"
}

Statt das sourceImplementierungsskript direkt im abhängigen Skript 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 Abhängigkeit eingefügt haben, die archive_filesmithilfe von taroder implementiert xz.

Beispiel: DI entfernen

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"

Nun besteht das Problem darin, dass die abhängige "Komponente" die Initialisierung selbst durchführen muss.

Der Quellcode der "Komponente" ist weder sauber noch stabil, da für jede Änderung der Initialisierung von Abhängigkeiten ein neues Release für die Quellcodedatei von "Komponenten" erforderlich ist.

Letzte Worte

DI wird nicht so stark betont und popularisiert wie in Java-Frameworks.

Es ist jedoch ein generischer Ansatz, um die Bedenken zu trennen:

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

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


Die beste Analogie, die ich mir vorstellen kann, ist der Chirurg und sein Assistent (en) in einem Operationssaal, in dem der Chirurg die Hauptperson und sein Assistent ist, der die verschiedenen chirurgischen Komponenten zur Verfügung stellt, wenn er sie benötigt, damit der Chirurg sich auf den einen konzentrieren kann was er am besten kann (Operation). Ohne den Assistenten muss der Chirurg die Komponenten jedes Mal selbst holen, wenn er eines benötigt.

DI, kurz DI, ist eine Technik, um eine gemeinsame zusätzliche Verantwortung (Belastung) für Komponenten zu beseitigen, um die abhängigen Komponenten abzurufen, indem sie ihnen zur Verfügung gestellt werden.

DI bringt Sie dem Single-Responsibility-Prinzip (SR) wie dem surgeon who can concentrate on surgery.

Wann sollten Sie DI verwenden: 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 einfach testbar ist, verspottet usw., damit Sie Ihre Änderungen schnell testen und auf den Markt bringen können. Abgesehen davon, warum sollten Sie es nicht tun, wenn Sie viele tolle kostenlose Tools / Frameworks zur Verfügung stellen, die Sie auf Ihrem Weg zu einer Codebasis unterstützen, in der Sie mehr Kontrolle haben.


Ich würde eine etwas andere, kurze und genaue Definition dessen vorschlagen, was Abhängigkeitsinjektion ist, und sich auf das primäre Ziel konzentrieren, nicht auf die technischen Mittel (von here ):

Abhängigkeitsinjektion ist der Prozess des Erstellens des statischen, zustandslosen Graphen von Serviceobjekten, wobei jeder Service durch seine Abhängigkeiten parametrisiert wird.

Die Objekte, die wir in unseren Anwendungen erstellen (unabhängig davon, ob wir Java, C # oder eine andere objektorientierte Sprache verwenden) fallen normalerweise in eine von zwei Kategorien: stateless, statische und globale "Service-Objekte" (Module) sowie Stateful, Dynamic und Local "Datenobjekte".

Das Moduldiagramm - das Diagramm der Serviceobjekte - wird normalerweise beim Start der Anwendung erstellt. Dies kann mit einem Container wie Spring geschehen, kann aber auch manuell erfolgen, indem Parameter an Objektkonstruktoren übergeben werden. Beide Möglichkeiten haben ihre Vor- und Nachteile, aber es ist definitiv nicht notwendig, DI in Ihrer Anwendung zu verwenden.

Eine Voraussetzung ist, dass die Dienste anhand ihrer Abhängigkeiten parametrisiert werden müssen. Was dies genau bedeutet, hängt von der Sprache und dem Ansatz eines bestimmten Systems ab. Normalerweise hat dies die Form von Konstruktorparametern, die Verwendung von Setters ist jedoch auch eine Option. Dies bedeutet auch, dass die Abhängigkeiten eines Dienstes (beim Aufrufen einer Dienstmethode) für die Benutzer des Dienstes verborgen sind.

Wann verwenden? Ich würde sagen, wann immer die Anwendung groß genug ist, um die Logik in separate Module zu kapseln, wobei ein Abhängigkeitsgraph zwischen den Modulen einen Gewinn an Lesbarkeit und Erkundbarkeit des Codes ergibt.


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

Keine Abhängigkeit:

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

Abhängigkeit:

public class Client {
    Service service;

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

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

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


Konzept der Abhängigkeitsinjektion verständlich machen. Nehmen wir ein Beispiel für den Schalterknopf, um eine Glühlampe ein- oder auszuschalten.

Ohne Abhängigkeitsinjektion

Switch muss vorher wissen, mit welcher Glühlampe ich verbunden bin (hartcodierte Abhängigkeit). So,

Schalter -> PermanentBulb // Der Schalter ist direkt an eine permanente Glühlampe angeschlossen, Tests sind nicht ohne weiteres möglich

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

Mit Abhängigkeitsinjektion

Switch weiß nur, dass ich die Bulb, die mir übergeben wird, ein- / ausschalten muss. So,

Schalter -> Birne1 ODER Birne2 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(); 
    ... 
  } 
}`

Wir haben beispielsweise 2 Klassen Clientund Service. Clientwird benutzenService

public class MyDao {

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

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

}

Ohne Abhängigkeitsinjektion

Weg 1)

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)
  {...}

}

Weg 2)

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

Weg 3)

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

1) 2) 3) Verwenden von

gcc foo.o bar.o -o bar

Vorteile

  • Einfach

Nachteile

  • Hart für ClientTestklasse
  • Wenn wir ändern ServiceKonstruktor, müssen wir Code in jedem Ort erstellen ändern ServiceObjekt

Verwenden Sie Abhängigkeitsinjektion

Weg 1) Konstruktorinjektion

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

Verwenden

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

Weg 2) Setterinjektion

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

Verwenden

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

Weg 3) Schnittstelleninjektion

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

===

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

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

Verwenden

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();
    }
}

2) Verwenden Sie die Bibliothek: Für Android dagger2

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, wenn Sie auf Konstruktor von schauen Client, sehen Sie, wie viele ClientKlassenabhängigkeiten vorhanden sind

Nachteile

  • Wenn Sie use verwenden Constructor Injection, wird das ServiceObjekt erstellt, wenn es Clienterstellt wird. 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 das Weitergeben einer Abhängigkeit ( Service) an ein abhängiges Objekt ( Client), das sie verwenden würde





terminology