Ich versuche, mit IoC / Dependency Injection umzugehen, während ich gleichzeitig auf Verträge und nicht auf bestimmte Klassen programmiere. Das Dilemma, das ich habe, ist die Spannung zwischen:
Programme für IoC-Schnittstellen programmieren : Ich habe mit IoC angefangen, die stark auf Schnittstellen basieren. Nach den Beispielprojekten von Spring zu urteilen, sind Schnittstellen die richtige Wahl, wenn man einen Vertrag mit IoC erstellt.
( ... obwohl abstrakte Klassen im Allgemeinen bevorzugt werden : Der Hauptnachteil von Schnittstellen ist, dass sie viel weniger flexibel als Klassen sind, wenn es um die Entwicklung von APIs geht )
Machen Sie Klassenabhängigkeiten über Konstruktor explizit Mein Bauchgefühl ist, dass es eine gute Programmierpraxis ist, Abhängigkeiten an den Konstruktor einer Klasse zu übergeben. Tatsächlich ist dies eine Abhängigkeitsinjektion.
... außer dass Sie die Konstruktorsignatur nicht in Schnittstellen / abstrakten Klassen erzwingen können : Weder Schnittstellen noch abstrakte Klassen ermöglichen die Definition einer Konstruktorsignatur (leicht / elegant) . Siehe auch Framework Design Guidelines Abschnitt 4.4: NICHT definieren public oder geschützte interne Konstruktoren in abstrakten Typen. ... Konstruktoren sollten nur öffentlich sein, wenn Benutzer Instanzen des Typs erstellen müssen.
Diese Frage bezieht sich auf eine vorherige Stackoverflow-Frage: Interface, das eine Konstruktorsignatur definiert? >
Aber meine Frage ist:
Da Sie einen Konstruktor in einer C # -Schnittstelle / abstrakten Klasse nicht definieren können, wie es die obige Frage erfordert, auf praktischer Ebene:
Wie vereinbaren Sie dies mit der sinnvollen Praxis, Abhängigkeiten über einen Konstruktor zu übergeben ?
Bearbeiten: Danke für die Antworten. Ich hoffe auf einen Einblick, was ich in diesem Fall tun sollte. Verwenden Sie einfach keine Konstruktorargumente? Verwenden Sie eine Art Init () -Methode, die die Abhängigkeiten übernimmt? Edit2: Danke für die tollen Antworten, sehr hilfreich.
Ich denke immer, das ist leichter zu erklären mit einem (erfundenen) Beispiel ...
Stellen Sie sich vor, Sie haben eine ICustomerRepository-Schnittstelle, eine IShoppingCartRepository-Schnittstelle und eine ICheckout-Schnittstelle. Sie haben konkrete Implementierungen dieser Schnittstellen - CustomerRepository, ShoppingCartRepository und CheckoutService.
Ihre konkrete CheckoutService-Klasse verfügt über einen Konstruktor, der ein ICustomerRepository und ein IShoppingCartRepository verwendet - z. B.
%Vor%Wenn Sie möchten, dass eine ICheckoutService-Implementierung mit Ihnen arbeitet, teilen Sie Ihrem IoC-Container mit, welche konkrete Klasse er für jeden Schnittstellentyp verwenden soll, und bitten Sie, einen ICheckoutService zu erstellen. Ihr IoC-Container wird Ihre Klassen für Sie erstellen und die richtigen konkreten Klassen in den Konstruktor Ihres Google CheckoutService einfügen. Es baut auch Abhängigkeiten bis hin zur Klassenhierarchie auf. Wenn Ihr ShoppingCartRepository beispielsweise eine IDatabaseSession-Schnittstelle im Konstruktor verwendet, wird Ihr IoC-Container diese Abhängigkeit ebenfalls einfügen, solange Sie ihm gesagt haben, welche konkrete Klasse verwendet werden soll für Ihren IDatabaseService.
Hier ist ein Beispielcode, den Sie verwenden können, wenn Sie beispielsweise StructureMap als Ihren IoC-Container konfigurieren (dieser Code wäre normalerweise) beim App-Start aufgerufen):
%Vor%Um eine Instanz von ICheckoutService zu erstellen und bereit zu machen, mit allen Abhängigkeiten, die für Sie in den Konstruktor übergeben werden, würden Sie etwas wie:
verwenden %Vor%Ich hoffe, das ergibt Sinn!
Ihr IoC-Container muss ein Objekt aus einem konkreten Typ konstruieren, obwohl das, was Sie passieren, eine Schnittstelle ist. Ihr Konstruktor ist kein Verhaltens- oder Statusvertrag, also gehört er nicht in eine Schnittstelle oder als öffentliches Mitglied Ihrer abstrakten Klasse.
Ein Konstruktor ist ein Implementierungsdetail, sodass Sie seine Definition nicht von der konkreten Klasse trennen müssen.
Sie können Konstruktorsignaturen in Schnittstellen nicht definieren. Das würde ohnehin keinen Sinn ergeben, da die Schnittstelle nicht die Implementierung der Implementierungen erzwingen sollte.
Abstrakte Klassen können jedoch Konstruktoren haben. Sie müssen geschützt werden, da auch öffentliche Konstrukteure keinen Sinn ergeben. Sie sollten nur von konkreten Unterklassen aufgerufen werden.
Das IoC-Prinzip schreibt vor, dass statt der Klasse A die Klasse B bekannt sein und instanziiert werden sollte, stattdessen sollte eine Referenz auf IB an den Konstruktor von A übergeben werden. Dann muss A nichts über die Klasse B wissen, und Sie können es leicht Ersetzen Sie Klasse B durch eine andere Implementierung von IB.
Da Sie ein bereits instanziiertes Objekt der Klasse B übergeben, benötigt die IB-Schnittstelle keine Konstruktorsignatur.
Tags und Links c# dependency-injection interface abstract-class inversion-of-control