Den Dependency Injector durch Dependency Injection injizieren

8

Ganz neu bei der Abhängigkeitsinjektion und ich versuche herauszufinden, ob dies ein Anti-Muster ist.

Nehmen wir an, ich habe 3 Assemblys:

%Vor%

Foo.Users benötigt ein Objekt, das in Foo.Payment erstellt wird, und Foo.Payment benötigt auch Sachen von Foo.Users. Dies schafft eine Art zirkuläre Abhängigkeit.

Ich habe eine Schnittstelle in Foo.Shared definiert, die das von mir verwendete Dependency-Injection-Framework (in diesem Fall NInject) als Proxy verwendet.

%Vor%

In der Containeranwendung habe ich eine Implementierung dieser Schnittstelle:

%Vor%

Die Konfiguration sieht folgendermaßen aus:

%Vor%

Damit kann ich ein neues Objekt von Foo.Payment.SomeType aus Foo.Users instanziieren, ohne eine direkte Referenz zu benötigen:

%Vor%

Das macht es unklar, was die genauen Abhängigkeiten der UserAccounts -Klasse in diesem Fall sind, was mich glauben macht, dass es keine gute Übung ist.

Wie kann ich das sonst noch schaffen?

Irgendwelche Gedanken?

    
andreialecu 12.12.2009, 00:50
quelle

3 Antworten

7

Obwohl etwas umstritten: Ja, das ist ein Anti-Muster. Es ist bekannt als Service Locator und während einige es für ein richtiges Designmuster halten, halte ich es für ein Anti-Pattern.

Dieses Problem besteht darin, dass z. Ihre UserAccounts-Klasse wird implizit anstatt explizit . Während der Konstruktor angibt, dass er einen IDependencyResolver benötigt, gibt er nicht an, was in ihm enthalten sein soll. Wenn Sie ein IDependencyResolver übergeben, das ISomeType nicht auflösen kann, wird es ausgelöst.

Schlimmer ist, dass Sie bei späteren Iterationen versucht sein könnten, einen anderen anderen Typ in UserAccounts aufzulösen. Es wird gut kompilieren, aber wird wahrscheinlich zur Laufzeit werfen, wenn / wenn der Typ nicht aufgelöst werden kann.

Geh nicht diesen Weg.

Aus den gegebenen Informationen ist es unmöglich, Ihnen genau zu sagen, wie Sie Ihr spezielles Problem mit zirkulären Abhängigkeiten lösen sollten, aber ich würde vorschlagen, dass Sie Ihr Design überdenken. In vielen Fällen sind zirkuläre Referenzen ein Symptom von Leaky Abstractions . Wenn Sie also Ihre API ein wenig umgestalten, wird sie verschwinden - es ist oft überraschend, wie kleine Änderungen erforderlich sind.

Im Allgemeinen besteht die Lösung für ein Problem darin, eine weitere Indirektionsebene hinzuzufügen. Wenn Objekte aus beiden Bibliotheken wirklich eng zusammenarbeiten müssen, können Sie in der Regel einen Zwischenbroker einrichten.

  • In vielen Fällen funktioniert das Publish / subscribe -Modell gut.
  • Das Mediator -Muster kann eine Alternative bieten, wenn die Kommunikation in beide Richtungen gehen muss.
  • Sie können auch eine Abstract Factory einführen, um die benötigte Instanz nach Bedarf zu erhalten, anstatt dass sie sofort verdrahtet werden muss.
Mark Seemann 12.12.2009, 08:21
quelle
2

Ich stimme ForeverDebugging zu - es wäre gut, die zirkuläre Abhängigkeit zu eliminieren. Sehen Sie, ob Sie die Klassen wie folgt trennen können:

  • Foo.Payment.dll: Klassen, die nur mit der Bezahlung arbeiten, nicht mit Benutzern
  • Foo.Users.dll: Klassen, die nur mit Benutzern arbeiten, nicht mit Zahlung
  • Foo.UserPayment.dll: Klassen, die sich sowohl mit Zahlungen als auch mit Benutzern befassen

Dann haben Sie eine Assembly, die auf zwei andere verweist, aber keinen Kreis von Abhängigkeiten.

Wenn Sie eine zirkuläre Abhängigkeit zwischen Assemblys haben, bedeutet dies nicht unbedingt, dass Sie eine zirkuläre Abhängigkeit zwischen Klassen haben. Angenommen, Sie haben diese Abhängigkeiten:

  • Foo.Users.UserAccounts hängt von Foo.Shared.IPaymentHistory ab, das von Foo.Payment.PaymentHistory implementiert wird.
  • Eine andere Zahlungsklasse, Foo.Payment.PaymentGateway, hängt von Foo.Shared.IUserAccounts ab. IUserAccounts wird von Foo.Users.UserAccounts implementiert.

Angenommen, es gibt keine anderen Abhängigkeiten.

Hier gibt es einen Kreis von Assemblys, die zur Laufzeit in Ihrer Anwendung aufeinander angewiesen sind (obwohl sie zur Kompilierzeit nicht aufeinander angewiesen sind, da sie die gemeinsame DLL durchlaufen). Aber es gibt keinen Kreis von Klassen, die voneinander abhängen, zur Kompilierungszeit oder zur Laufzeit.

In diesem Fall sollten Sie Ihren IoC-Container weiterhin normal verwenden können, ohne eine zusätzliche Indirektionsebene hinzuzufügen. In Ihrem MyModule binden Sie einfach jede Schnittstelle an den entsprechenden konkreten Typ. Lassen Sie jede Klasse ihre Abhängigkeiten als Argumente für den Konstruktor akzeptieren. Wenn Ihr Top-Level-Anwendungscode eine Instanz einer Klasse benötigt, fragen Sie den IoC-Container nach der Klasse. Lassen Sie den IoC-Container sich darum kümmern, alles zu finden, von dem die Klasse abhängt.



Wenn Sie eine zirkuläre Abhängigkeit zwischen Klassen haben, müssen Sie wahrscheinlich die Injektion von Eigenschaften (Setterinjektion) für eine der Klassen anstelle der Konstruktorinjektion verwenden. Ich benutze Ninject nicht, aber es unterstützt die Injektion von Eigenschaften - hier ist die Dokumentation .

Normalerweise verwenden IoC-Container die Konstruktorinjektion - sie übergeben Abhängigkeiten an den Konstruktor der Klasse, die von ihnen abhängt. Dies funktioniert jedoch nicht, wenn eine zirkuläre Abhängigkeit besteht. Wenn die Klassen A und B voneinander abhängig sind, müssen Sie eine Instanz der Klasse A in dem Konstruktor der Klasse B übergeben. Um jedoch ein A zu erstellen, müssen Sie eine Instanz der Klasse B in den Konstruktor übergeben. Es ist ein Huhn-und-Ei-Problem.

Bei der Eigenschafteninjektion weisen Sie Ihren IoC-Container an, zuerst den Konstruktor aufzurufen und dann eine Eigenschaft für das konstruierte Objekt festzulegen. Normalerweise wird dies für optionale Abhängigkeiten wie Logger verwendet. Sie können es aber auch verwenden, um eine zirkuläre Abhängigkeit zwischen zwei Klassen zu unterbrechen, die einander erfordern.

Das ist jedoch nicht schön, und ich würde definitiv empfehlen, Ihre Klassen zu refactorieren, um zirkuläre Abhängigkeiten zu eliminieren.

    
Richard Beier 12.12.2009 03:25
quelle
1

Das scheint mir etwas seltsam zu sein. Ist es möglich, die Logik, die beide Referenzen benötigt, in eine dritte Assembly zu zerlegen, um die Abhängigkeiten aufzubrechen und das Risiko zu vermeiden?

    
Kye 12.12.2009 02:26
quelle