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.
Antworten:
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
DataRepository
Prüfung ablegen, aber beim Testen würde ich eine Prüfung ablegenFakeDataRepository
. In diesem Fall stelle ich normalerweise zwei Konstruktoren zur Verfügung: einen ohne Parameter und einen anderen, der a akzeptiertIDataRepository
. Dann werde ich im Konstruktor ohne Parameter einen Aufruf an den zweiten Konstruktor ketten und a übergebennew DataRepository()
.Hier ist ein Beispiel in C #:
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
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.quelle
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
SoftReference
in 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).
quelle