Was ist der praktische Unterschied zwischen den Arten der Abhängigkeitsinjektion?

12

Ich bin neu in der Abhängigkeitsinjektion und habe ein paar Fragen dazu, welchen Stil ich in meinen Anwendungen verwenden soll. Ich habe gerade Inversion of Control Containers und das Dependency Injection Pattern von Martin Fowler gelesen , kann aber den praktischen Unterschied zwischen Konstruktor-, Setter- und Interface-Injection nicht erkennen.

Es scheint mir, dass die Gründe für die Verwendung übereinander nur eine Frage der Code-Bereinigung und / oder Klarheit sind. Was ist der Unterschied? Gibt es irgendwelche starken Vor- oder Nachteile, wenn man eins gegenüber dem anderen verwendet, oder ist es genau das, was ich zuvor angegeben habe?

Meiner Meinung nach ist die Konstruktor-Injection die intuitivste und die Interface-Injection die geringste. Auf der anderen Seite ist die Setter-Injection ein Mittelbegriff, aber soll es Ihnen möglich sein, die Instanz des Abhängigkeitsobjekts zu ändern, das Sie ursprünglich injiziert haben? Garantiert diese Art der Injektion, dass das Objekt, das die Abhängigkeit benötigt, immer injiziert wird? Ich glaube nicht, aber bitte korrigiere mich, wenn ich falsch liege.

ecampver
quelle
Nicht sicher, was Sie sonst noch gefunden oder gelesen haben. Ich habe hier und hier ähnliche Themen gefunden . Ich selbst bin neu und bin gespannt auf die Antworten, die Sie möglicherweise erhalten :)
@ user1766760 du solltest mir dann hier weiterhelfen, meine frage wird zur schließung gewählt, zwei weitere stimmen und es ist geschafft !!! Stimmen Sie die Frage ab oder so, ich weiß nicht, was getan werden kann, um ein Schließen zu vermeiden.
Ecampver
ähm ... Ich bin neu in SO selbst, also bin ich mir nicht sicher, wie ich helfen kann / warum für das Schließen abgestimmt wird (ich sehe keine Hinweise ??). Wenn ich eine Vermutung wage, ist es vielleicht keine spezielle Programmierfrage, sondern eher eine Diskussion?
Vielleicht möchten Sie die Frage etwas erweitern. Die Abhängigkeitsinjektion ist eine Form der "Inversion of Control". Es gibt Alternativen. Eine nützliche, aber missbrauchsanfällige Alternative ist beispielsweise der Service-Standort.
Ian

Antworten:

12

Konstruktorinjektion hat den Vorteil, dass sie die Abhängigkeit explizit macht und den Client zwingt, eine Instanz bereitzustellen. Es kann auch garantiert werden, dass der Client die Instanz später nicht ändern kann. Ein (möglicher) Nachteil ist, dass Sie Ihrem Konstruktor einen Parameter hinzufügen müssen.

Setter Injection hat den Vorteil, dass dem Konstruktor kein Parameter hinzugefügt werden muss. Es ist auch nicht erforderlich, dass der Client die Instanz festlegt. Dies ist nützlich für optionale Abhängigkeiten. Dies kann auch nützlich sein, wenn Sie möchten, dass die Klasse standardmäßig beispielsweise ein reales Datenrepository erstellt. In einem Test können Sie den Setter dann verwenden, um ihn durch eine Testinstanz zu ersetzen.

Interface Injection ist , soweit ich das beurteilen kann, nicht viel anders als Setter Injection. In beiden Fällen legen Sie (optional) eine Abhängigkeit fest, die später geändert werden kann.

Letztendlich ist es eine Frage der Präferenz und ob eine Abhängigkeit erforderlich ist oder nicht . Persönlich verwende ich fast ausschließlich Konstruktor-Injection. Mir gefällt, dass dadurch die Abhängigkeiten einer Klasse explizit werden, indem der Client gezwungen wird, eine Instanz im Konstruktor bereitzustellen. Mir gefällt auch, dass der Client die Instanz nicht nachträglich ändern kann.

Oft ist mein einziger Grund, zwei separate Implementierungen zu übergeben, das Testen. In der Produktion kann ich eine DataRepositoryPrüfung ablegen, aber beim Testen würde ich eine Prüfung ablegen FakeDataRepository. In diesem Fall stelle ich normalerweise zwei Konstruktoren zur Verfügung: einen ohne Parameter und einen anderen, der a akzeptiert IDataRepository. Dann werde ich im Konstruktor ohne Parameter einen Aufruf an den zweiten Konstruktor ketten und a übergeben new DataRepository().

Hier ist ein Beispiel in C #:


public class Foo
{
  private readonly IDataRepository dataRepository;

  public Foo() : this(new DataRepository())
  {
  }

  public Foo(IDataRespository dataRepository)
  {
    this.dataRepository = dataRepository;
  }
}

Dies ist als Poor Man's Dependency Injection bekannt. Ich mag es, weil ich mich im Code des Produktionsclients nicht wiederholen muss, indem ich mehrere wiederholte Anweisungen habe, die aussehen

var foo = new Foo(new DataRepository());
Ich kann jedoch immer noch eine alternative Implementierung zum Testen übergeben. Mir ist klar, dass ich mit Poor Man's DI meine Abhängigkeit fest codiere, aber das ist für mich akzeptabel, da ich DI hauptsächlich zum Testen verwende.

jhewlett
quelle
Danke, das ist ganz in der Nähe zu dem, was ich verstehe, aber immer noch gibt es irgendeine Szenario , in dem Sie nicht können verwenden Konstruktor Injektion ?
Ecampver
1
Sie möchten wahrscheinlich keine Konstruktorinjektion für optionale Abhängigkeiten verwenden. Ich kann mir keine anderen Gründe vorstellen, es nicht zu benutzen.
Jhewlett
Ich verwende Property-Set-Injection speziell, um einige Konfigurationsklassen zu füllen, die eine große Anzahl von Werten enthalten. Ich benutze es nirgendwo anders. Ich bin immer ein bisschen zweifelhaft, ob es darum geht, optionale Parameter in Frage zu stellen, da die Wahrscheinlichkeit groß ist, dass dies einen Verstoß gegen die Einzelverantwortungsregel darstellt. Natürlich sollen Regeln gebrochen werden, also ...
Ian
@jhewlett - das ist fantastisch, ich habe nach einer guten einfachen Stubbing-Technik für Unit-Tests gesucht, die meine Lösung nicht übermäßig kompliziert - und ich denke, das ist es :)
Rocklan
2

Die Unterschiede zwischen Konstruktor- und Setter-Injection sind oben bereits ausreichend beschrieben, daher werde ich nicht weiter darauf eingehen.

Interface-Injection ist eine weiterentwickelte Form der Injection, die nützlich ist, da die Abhängigkeit zum Zeitpunkt der Verwendung festgelegt werden kann und nicht während der Initialisierung des Objekts, das sie verwendet. Dies ermöglicht eine Reihe nützlicher Optionen:

  • Die Abhängigkeit kann sich unterschiedlich auf das Objekt auswirken, in das sie injiziert wird. Beispielsweise können Sie mithilfe der Schnittstelleninjektion einem globalen Singleton ein Objekt bereitstellen, für das ein Objekt pro Benutzersitzung oder pro Thread vorhanden ist. Jedes Mal, wenn das Objekt die Abhängigkeit benötigt, ruft es die vom Framework bereitgestellte Getter-Methode auf. Dies kann je nach der Situation, in der es aufgerufen wird, unterschiedliche Ergebnisse zurückgeben.

  • Dies ermöglicht eine verzögerte Initialisierung. Die Abhängigkeit muss erst initialisiert werden, wenn sie verwendet wird

  • Abhängigkeiten können aus einer zwischengespeicherten Kopie geladen werden, wenn sie vorhanden sind, oder wenn sie nicht neu initialisiert werden (z. B. mithilfe von a SoftReferencein Java).

Offensichtlich haben fortgeschrittene Techniken wie diese Nachteile; In diesem Fall besteht das Hauptproblem darin, dass der Code weniger klar wird (in Ihrem Code verwendete Klassen werden abstrakt, und es gibt keine offensichtliche konkrete Implementierung von ihnen, was verwirrend sein kann, wenn Sie nicht daran gewöhnt sind) und dass Sie abhängiger werden Dies hängt von Ihrem Abhängigkeits-Injection-Framework ab (es ist natürlich immer noch möglich , Ihre Objekte manuell zu instanziieren, aber es ist schwieriger als mit anderen Injection-Styles).

Jules
quelle