Ich überarbeite eine Klasse und füge ihr eine neue Abhängigkeit hinzu. Die Klasse übernimmt derzeit ihre vorhandenen Abhängigkeiten im Konstruktor. Aus Gründen der Konsistenz füge ich den Parameter dem Konstruktor hinzu.
Natürlich gibt es ein paar Unterklassen und noch mehr für Unit-Tests, also spiele ich jetzt das Spiel, alle Konstruktoren so zu ändern, dass sie übereinstimmen, und es dauert ewig.
Ich denke, dass die Verwendung von Eigenschaften mit Setzern ein besserer Weg ist, um Abhängigkeiten zu erhalten. Ich denke nicht, dass injizierte Abhängigkeiten Teil der Schnittstelle zum Erstellen einer Instanz einer Klasse sein sollten. Sie fügen eine Abhängigkeit hinzu und jetzt wissen plötzlich alle Ihre Benutzer (Unterklassen und alle, die Sie direkt instanziieren) davon. Das fühlt sich wie eine Unterbrechung der Kapselung an.
Dies scheint nicht das Muster mit dem hier vorhandenen Code zu sein, daher möchte ich herausfinden, was der allgemeine Konsens ist, welche Vor- und Nachteile Konstruktoren gegenüber Eigenschaften haben. Ist die Verwendung von Eigenschaftssetzern besser?
quelle
Die Benutzer einer Klasse sollen über die Abhängigkeiten einer bestimmten Klasse Bescheid wissen. Wenn ich eine Klasse hätte, die beispielsweise mit einer Datenbank verbunden ist und keine Möglichkeit bietet, eine Persistenzschichtabhängigkeit einzufügen, würde ein Benutzer niemals wissen, dass eine Verbindung zur Datenbank verfügbar sein muss. Wenn ich jedoch den Konstruktor ändere, lasse ich die Benutzer wissen, dass eine Abhängigkeit von der Persistenzschicht besteht.
Um zu verhindern, dass Sie jede Verwendung des alten Konstruktors ändern müssen, wenden Sie einfach die Konstruktorkettung als temporäre Brücke zwischen dem alten und dem neuen Konstruktor an.
Einer der Punkte der Abhängigkeitsinjektion besteht darin, aufzuzeigen, welche Abhängigkeiten die Klasse hat. Wenn die Klasse zu viele Abhängigkeiten aufweist, kann es Zeit für ein Refactoring sein: Verwendet jede Methode der Klasse alle Abhängigkeiten? Wenn nicht, ist dies ein guter Ausgangspunkt, um zu sehen, wo die Klasse aufgeteilt werden könnte.
quelle
Wenn Sie den Konstruktor aktivieren, können Sie natürlich alle auf einmal validieren. Wenn Sie Dinge in schreibgeschützten Feldern zuweisen, haben Sie ab der Erstellungszeit einige Garantien für die Abhängigkeiten Ihres Objekts.
Es ist ein echtes Problem, neue Abhängigkeiten hinzuzufügen, aber zumindest auf diese Weise beschwert sich der Compiler so lange, bis es korrekt ist. Welches ist eine gute Sache, denke ich.
quelle
Wenn Sie eine große Anzahl optionaler Abhängigkeiten haben (was bereits ein Geruch ist), ist wahrscheinlich die Setter-Injektion der richtige Weg. Die Konstruktorinjektion zeigt jedoch besser Ihre Abhängigkeiten.
quelle
Der allgemein bevorzugte Ansatz besteht darin, die Konstruktorinjektion so weit wie möglich zu verwenden.
Die Konstruktorinjektion gibt genau an, welche Abhängigkeiten erforderlich sind, damit das Objekt ordnungsgemäß funktioniert. Nichts ist ärgerlicher, als ein Objekt neu zu erstellen und es beim Aufrufen einer Methode zum Absturz zu bringen, da einige Abhängigkeiten nicht festgelegt sind. Das von einem Konstruktor zurückgegebene Objekt sollte sich in einem funktionierenden Zustand befinden.
Versuchen Sie, nur einen Konstruktor zu haben. Dies hält das Design einfach und vermeidet Mehrdeutigkeiten (wenn nicht für Menschen, für den DI-Container).
Sie können die Eigenschaftsinjektion verwenden, wenn Sie das haben, was Mark Seemann als lokalen Standard bezeichnet in seinem Buch "Abhängigkeitsinjektion in .NET" : Die Abhängigkeit ist optional, da Sie eine gut funktionierende Implementierung bereitstellen können, dem Aufrufer jedoch erlauben möchten, eine andere anzugeben, wenn erforderlich.
(Frühere Antwort unten)
Ich denke, dass die Konstruktorinjektion besser ist, wenn die Injektion obligatorisch ist. Wenn dadurch zu viele Konstruktoren hinzugefügt werden, sollten Sie Fabriken anstelle von Konstruktoren verwenden.
Die Setter-Injektion ist gut, wenn die Injektion optional ist oder wenn Sie sie auf halbem Weg ändern möchten. Ich mag Setter im Allgemeinen nicht, aber es ist Geschmackssache.
quelle
Es ist größtenteils eine Frage des persönlichen Geschmacks. Persönlich bevorzuge ich die Setter-Injektion, weil ich glaube, dass sie Ihnen mehr Flexibilität bietet, wenn Sie Implementierungen zur Laufzeit ersetzen können. Darüber hinaus sind Konstruktoren mit vielen Argumenten meiner Meinung nach nicht sauber, und die in einem Konstruktor angegebenen Argumente sollten auf nicht optionale Argumente beschränkt sein.
Solange die Klassenschnittstelle (API) klar ist, was sie zur Ausführung ihrer Aufgabe benötigt, sind Sie gut.
quelle
Ich persönlich bevorzuge das "Muster" " Extrahieren und Überschreiben " gegenüber dem Einfügen von Abhängigkeiten in den Konstruktor, hauptsächlich aus dem in Ihrer Frage dargelegten Grund. Sie können die Eigenschaften als festlegen
virtual
und dann die Implementierung in einer abgeleiteten testbaren Klasse überschreiben.quelle
Ich bevorzuge die Konstruktorinjektion, weil sie dazu beiträgt, die Abhängigkeitsanforderungen einer Klasse zu "erzwingen". Wenn es in der c'tor ist ein Verbraucher hat die Objekte , um den App Kompilierung zu erhalten. Wenn Sie die Setter-Injektion verwenden, wissen sie möglicherweise erst zur Laufzeit, dass sie ein Problem haben - und je nach Objekt kann es zu spät zur Laufzeit kommen.
Ich verwende immer noch von Zeit zu Zeit die Setter-Injektion, wenn das injizierte Objekt möglicherweise selbst eine Menge Arbeit benötigt, wie z. B. die Initialisierung.
quelle
Ich bevorzuge die Konstruktorinjektion, weil dies am logischsten erscheint. Es ist so, als würde ich sagen, dass meine Klasse diese Abhängigkeiten benötigt , um ihre Arbeit zu erledigen. Wenn es sich um eine optionale Abhängigkeit handelt, erscheinen die Eigenschaften vernünftig.
Ich verwende die Eigenschaftsinjektion auch zum Festlegen von Dingen, auf die der Container keinen Verweis hat, z. B. eine ASP.NET-Ansicht in einem mit dem Container erstellten Präsentator.
Ich glaube nicht, dass es die Kapselung bricht. Das Innenleben sollte intern bleiben und die Abhängigkeiten befassen sich mit einem anderen Anliegen.
quelle
Eine Option, die in Betracht gezogen werden könnte, ist das Zusammensetzen komplexer Mehrfachabhängigkeiten aus einfachen Einzelabhängigkeiten. Definieren Sie zusätzliche Klassen für zusammengesetzte Abhängigkeiten. Dies erleichtert die Injektion von WRT-Konstruktoren ein wenig - weniger Parameter pro Aufruf - und behält gleichzeitig die Notwendigkeit bei, alle Abhängigkeiten zu liefern, um sie zu instanziieren.
Am sinnvollsten ist es natürlich, wenn es eine logische Gruppierung von Abhängigkeiten gibt, sodass die Verbindung mehr als ein beliebiges Aggregat ist, und es ist am sinnvollsten, wenn es mehrere Abhängigkeiten für eine einzelne zusammengesetzte Abhängigkeit gibt - aber der Parameterblock "Muster" hat Ich bin schon lange da und die meisten, die ich gesehen habe, waren ziemlich willkürlich.
Persönlich bin ich jedoch eher ein Fan der Verwendung von Methoden / Eigenschaftssetzern, um Abhängigkeiten, Optionen usw. anzugeben. Die Anrufnamen beschreiben, was vor sich geht. Es ist jedoch eine gute Idee, ein Beispiel für das Einrichten von Snippets anzugeben und sicherzustellen, dass die abhängige Klasse genügend Fehlerprüfungen durchführt. Möglicherweise möchten Sie ein Finite-State-Modell für das Setup verwenden.
quelle
Ich war kürzlich in einer Situation, in der ich mehrere Abhängigkeiten in einer Klasse hatte, aber nur eine der Abhängigkeiten musste sich in jeder Implementierung ändern. Da die Abhängigkeiten von Datenzugriff und Fehlerprotokollierung wahrscheinlich nur zu Testzwecken geändert werden, habe ich optionale Parameter hinzugefügt für diese Abhängigkeiten und Standardimplementierungen dieser Abhängigkeiten in meinem Konstruktorcode bereitgestellt. Auf diese Weise behält die Klasse ihr Standardverhalten bei, sofern es nicht vom Verbraucher der Klasse überschrieben wird.
Die Verwendung optionaler Parameter kann nur in Frameworks durchgeführt werden, die sie unterstützen, wie z. B. .NET 4 (sowohl für C # als auch für VB.NET, obwohl VB.NET sie immer hatte). Natürlich können Sie ähnliche Funktionen erreichen, indem Sie einfach eine Eigenschaft verwenden, die vom Konsumenten Ihrer Klasse neu zugewiesen werden kann. Sie erhalten jedoch nicht den Vorteil der Unveränderlichkeit, wenn ein privates Schnittstellenobjekt einem Parameter des Konstruktors zugewiesen wird.
Wenn Sie jedoch eine neue Abhängigkeit einführen, die von jedem Verbraucher bereitgestellt werden muss, müssen Sie Ihren Konstruktor und den gesamten Code, der Ihre Klasse verwendet, umgestalten. Meine obigen Vorschläge gelten wirklich nur, wenn Sie den Luxus haben, eine Standardimplementierung für Ihren gesamten aktuellen Code bereitzustellen, aber dennoch die Möglichkeit bieten, die Standardimplementierung bei Bedarf zu überschreiben.
quelle
Dies ist ein alter Beitrag, aber wenn er in Zukunft benötigt wird, ist dies möglicherweise von Nutzen:
https://github.com/omegamit6zeichen/prinject
Ich hatte eine ähnliche Idee und kam auf diesen Rahmen. Es ist wahrscheinlich alles andere als vollständig, aber es ist eine Idee eines Frameworks, das sich auf die Eigenschaftsinjektion konzentriert
quelle
Dies hängt davon ab, wie Sie implementieren möchten. Ich bevorzuge die Konstruktorinjektion überall dort, wo ich der Meinung bin, dass sich die Werte, die in die Implementierung einfließen, nicht oft ändern. Beispiel: Wenn die Compnay-Strategie mit Oracle Server verbunden ist, konfiguriere ich meine Datenquellenwerte für eine Bean, die Verbindungen über Konstruktorinjektion erreicht. Andernfalls würde ich, wenn meine App ein Produkt ist und wahrscheinlich eine Verbindung zu einer beliebigen Datenbank des Kunden herstellen kann, eine solche Datenbankkonfiguration und Mehrmarkenimplementierung durch Setter-Injection implementieren. Ich habe gerade ein Beispiel genommen, aber es gibt bessere Möglichkeiten, die oben genannten Szenarien zu implementieren.
quelle
Die Konstruktorinjektion enthüllt explizit die Abhängigkeiten, wodurch Code lesbarer und weniger anfällig für nicht behandelte Laufzeitfehler wird, wenn Argumente im Konstruktor überprüft werden. Es kommt jedoch auf die persönliche Meinung an, und je mehr Sie DI verwenden, desto mehr werden Sie neigen dazu, je nach Projekt auf die eine oder andere Weise hin und her zu schwanken. Ich persönlich habe Probleme mit Code-Gerüchen wie Konstruktoren mit einer langen Liste von Argumenten, und ich bin der Meinung, dass der Verbraucher eines Objekts die Abhängigkeiten kennen sollte, um das Objekt trotzdem verwenden zu können. Daher ist dies ein Argument für die Verwendung der Eigenschaftsinjektion. Ich mag die implizite Natur der Eigenschaftsinjektion nicht, aber ich finde sie eleganter, was zu einem saubereren Code führt. Andererseits bietet die Konstruktorinjektion einen höheren Einkapselungsgrad.
Wählen Sie die Injektion nach Konstruktor oder Eigenschaft mit Bedacht aus, basierend auf Ihrem spezifischen Szenario. Und denken Sie nicht, dass Sie DI verwenden müssen, nur weil es notwendig erscheint und schlechtes Design und Code-Gerüche verhindert. Manchmal lohnt es sich nicht, ein Muster zu verwenden, wenn der Aufwand und die Komplexität den Nutzen überwiegen. Halte es einfach.
quelle