Warum brauchen wir einen Rahmen für den Dependency Resolver?

8

Ich habe immer gesehen, dass Leute immer davon sprachen, Frameworks wie Ninject, Unity, Windsor zu benutzen, um den Dependency Resolver und die Injektion zu machen. Nehmen Sie den folgenden Code zum Beispiel:

%Vor%

Meine Frage ist: Warum können wir nicht einfach schreiben als:

%Vor%

In diesem Fall brauchen wir keinen Rahmen, selbst für den Komponententest können wir leicht spotten.

Was ist der wahre Zweck dieses Frameworks?

Vielen Dank im Voraus!

    
Samuel 03.09.2014, 07:46
quelle

8 Antworten

3

In diesem Fall hängt Ihr ProductsController immer noch von einer Komponente auf niedriger Ebene ab (die konkrete ProductRepository in Ihrem Fall), die eine Verletzung der Dependency Inversion-Prinzip . Ob dies ein Problem ist oder nicht, hängt von mehreren Faktoren ab, verursacht jedoch die folgenden Probleme:

  • Die Erstellung von ProductRepository wird immer noch in der gesamten Anwendung dupliziert, was zu weitreichenden Änderungen in der gesamten Anwendung führt, wenn der Konstruktor ProductRepository chances (vorausgesetzt, dass ProductRepository an mehreren Stellen verwendet wird, was durchaus vernünftig ist) Das wäre ein Verletzung des offenen / geschlossenen Prinzips .
  • Es bewirkt, dass Sie umfassende Änderungen vornehmen, wenn Sie dieses ProductService mit einem Decorator oder Interceptor umhüllen, der übergreifende Probleme (wie Protokollierung, Nachverfolgung, Sicherheitsfilterung usw.) hinzufügt, die Sie sicher nicht möchten um diesen Code in allen Ihren Repositories zu wiederholen (wieder eine OCP-Verletzung).
  • Er zwingt die ProductsController , über die ProductsRepository zu wissen, welche möglicherweise ein Problem ist, abhängig von der Größe und Komplexität der Anwendung, die Sie schreiben.

Es geht also nicht um den Einsatz von Frameworks, sondern um die Anwendung von Software-Design-Prinzipien. Wenn Sie sich dazu entschließen, diese Prinzipien einzuhalten, um Ihre Anwendung wartbarer zu machen, können Ihnen Frameworks wie Ninject, Autofac und Simple Injector dabei helfen, den Startpfad Ihrer Anwendung besser zu verwalten. Aber nichts hindert Sie daran, diese Prinzipien ohne den Einsatz eines Werkzeugs oder einer Bibliothek anzuwenden.

    
Steven 03.09.2014, 08:19
quelle
3

Kleiner Haftungsausschluss: Ich bin ein begeisterter Unity-Benutzer und hier sind meine 2 Cent.

1 .: Verletzung von SOLID (SRP / OCP / DIP)

Bereits von @democodemonkey und @thumbmunkeys angegeben, koppeln Sie die 2 Klassen fest. Nehmen wir an, dass einige Klassen (seien es ProductsThingamajigOne und ProductsThingamajigTwo) den ProductsController verwenden und seinen Standardkonstruktor verwenden. Was ist, wenn der Architekt entscheidet, dass das System kein ProductsRepository verwenden soll, das Produkte in Dateien speichert, sondern eine Datenbank oder einen Cloud-Speicher verwenden soll? Was wäre der Einfluss auf die Klassen?

2nd: Was ist, wenn das ProductRepository eine andere Abhängigkeit benötigt?

Wenn das Repository auf einer Datenbank basiert, müssen Sie möglicherweise einen ConnectionString bereitstellen. Wenn es auf Dateien basiert, müssen Sie möglicherweise eine Reihe von Einstellungen angeben, die genau angeben, wo die Dateien gespeichert werden sollen. Tatsächlich enthalten Anwendungen im Allgemeinen Abhängigkeiten (A, die von B und C abhängen) , B abhängig von D, C abhängig von E, D abhängig von F und G und so weiter), die mehr als 2 Stufen haben, so verletzt die SOLID-Verletzung mehr, da mehr Code geändert werden muss, um eine Aufgabe zu erfüllen - aber schon vorher Kannst du dir den Code vorstellen, der die gesamte App erstellt? Fakt ist, dass Klassen viele Abhängigkeiten haben können - und in diesem Fall multiplizieren sich die oben beschriebenen Probleme.

Das ist normalerweise der Job des Bootstrappers - er definiert die Abhängigkeitsstruktur und führt (normalerweise) eine einzelne Lösung aus, die das ganze System wie eine Marionette auf einer Kette aufbaut.

3rd: Was ist, wenn der Dependency-Tree kein Baum ist, sondern ein Graph?

Betrachten wir den folgenden Fall: Klasse A, die von den Klassen B und C, B und C abhängig ist, hängt von der Klasse D ab und erwartet, dieselbe Instanz von D zu verwenden. Eine gängige Praxis war, D zu einem Singleton zu machen könnte eine Menge Probleme verursachen. Die andere Möglichkeit besteht darin, eine Instanz von D in den Konstruktor von A zu übergeben und B und C zu erstellen oder Instanzen von B und C an A weiterzugeben und sie außerhalb zu erstellen - und die Komplexität geht weiter und weiter.

4.: Verpackung (Baugruppen)

Ihr Code geht davon aus, dass 'ProductsController' 'ProductRepository' (montageweise) sehen kann. Was ist, wenn es keinen Bezug zwischen ihnen gibt? Die Assembly Map kann nicht trivial sein. In der Regel wird der Bootstrapping-Code (ich gehe davon aus, dass er sich für eine Sekunde in Code und nicht in der Konfigurationsdatei befindet) in eine Assembly geschrieben, die auf die gesamte Lösung verweist. (Dies wurde auch von @ Steven beschrieben).

5.: Coole Sachen, die man mit IoC-Containern machen kann

Singletons sind leicht gemacht (mit Einheit: benutzen Sie einfach einen 'containergesteuerten LIFEItemanager' bei der Registrierung), Lazy Instantiation gemacht wirklich einfach (mit Einheit: Register Mapping von und fragen Sie im Konstruktor für eine Func). Das sind nur ein paar Beispiele für Dinge, die Ihnen IoC-Container (fast) kostenlos zur Verfügung stellen.

HTH!

    
Felix 03.09.2014 08:58
quelle
2

Natürlich könnten Sie das tun, aber das würde die folgenden Probleme verursachen:

  • Die Abhängigkeit von IProductRepository ist nicht mehr explizit, sie sieht wie eine optionale Abhängigkeit aus
  • Andere Teile des Codes könnten eine andere Implementierung von IProductRepository instanziieren, was in diesem Fall wahrscheinlich ein Problem darstellen würde
  • Die Klasse wird eng an ProductsController gekoppelt, da sie intern eine Abhängigkeit
  • erstellt

Meiner Meinung nach handelt es sich nicht um einen Rahmen. Der Punkt besteht darin, Module zusammensetzbar zu machen, indem ihre Abhängigkeiten in einem Konstruktor oder einer Eigenschaft offengelegt werden. Ihr Beispiel verschleiert das etwas.

    
thumbmunkeys 03.09.2014 07:56
quelle
1

Wenn die Klasse ProductRepository nicht in derselben Assembly wie ProductsController definiert ist (oder wenn Sie sie jemals in eine andere Assembly verschieben möchten), haben Sie gerade eine Abhängigkeit eingeführt, die Sie nicht möchten.

Das ist ein Anti-Pattern, das im bahnbrechenden Buch "Dependency Injection in .Net" von Mark Seeman als "Bastard Injection" beschrieben wird.

Wenn ProductRepository IMMER jedoch in der selben Assembly wie ProductsController ist und wenn es nicht von irgendetwas abhängt, von dem der Rest der ProductsController Assembly abhängt, könnte es ein local default sein - In diesem Fall wäre es in Ordnung.

Von den Klassennamen wette ich, dass solch eine Abhängigkeit nicht eingeführt werden sollte, und Sie schauen auf Bastard Injektion.

    
Matthew Watson 03.09.2014 08:18
quelle
0

Hier ist ProductsController verantwortlich für die Erstellung von ProductRepository .

Was passiert, wenn ProductRepository einen zusätzlichen Parameter in seinem Konstruktor benötigt? Dann muss ProductsController geändert werden, und dies verletzt die SRP.

Zusätzlich zu mehr Komplexität für alle Ihre Objekte.

Sowie es unklar, ob ein Anrufer das Kind-Objekt übergeben muss, oder ist es optional?

    
demoncodemonkey 03.09.2014 07:57
quelle
0

Der Hauptzweck besteht darin, die Objekterstellung von ihrer Verwendung oder ihrem Verbrauch zu entkoppeln. Die Erstellung des Objekts "in der Regel" wird von Fabrikklassen übernommen. In Ihrem Fall werden die Factory-Klassen so entworfen, dass sie ein Objekt eines Typs zurückgeben, der IProductRepository interface implementiert.

In einigen Frameworks wie in Sprint.Net instanzieren die Factory-Klassen Objekte, die deklarativ in die Konfiguration geschrieben werden (z. B. in app.config oder web.config). Dadurch wird das Programm völlig unabhängig von dem Objekt, das es erstellen muss. Das kann manchmal sehr mächtig sein.

    
deostroll 03.09.2014 08:01
quelle
0

Es ist wichtig, die Abhängigkeitsinjektion zu unterscheiden und die Inversion der Kontrolle ist nicht die gleiche. Sie können die Abhängigkeitsinjektion ohne IOC-Frameworks wie unity, ninject ... verwenden, indem Sie die Injektion manuell ausführen, was sie oft DI des armen Mannes nannten.

In meinem Blog habe ich kürzlich einen Beitrag zu diesem Thema geschrieben Ссылка

Wenn ich auf Ihr Beispiel zurückkomme, sehe ich Schwächen in der Implementierung.

1 - ProductsController ist abhängig von einer Konkretion und nicht von einer Abstraktion, Verletzung SOLID

2 - Falls die Schnittstelle und das Repository in verschiedenen Projekten leben, müssen Sie einen Verweis auf das Projekt haben, in dem sich das Repository befindet

3 - Wenn Sie in der Zukunft einen Parameter zum Konstruktor hinzufügen müssen, müssten Sie den Controller ändern, wenn es sich um einen einfachen Repository-Client handelt.

4 - Controller und Repository können für verschiedene Programmierer entwickelt werden, Controller-Programmierer müssen wissen, wie Repository erstellen

    
xurxodev 05.09.2014 08:40
quelle
0

Betrachte diesen Anwendungsfall:

Angenommen, Sie möchten in Zukunft CustomProductRepository anstelle von ProductRepository bis ProductsController in die Software injizieren, die bereits auf der Client-Site implementiert ist.

Mit Spring.Net können Sie einfach die Spring-Konfigurationsdatei (xml) aktualisieren, um Ihre CustomProductRepository zu verwenden. So können Sie die Neukompilierung und Neuinstallation von Software auf der Client-Site vermeiden, da Sie keinen Code geändert haben.

    
manjuv 12.09.2015 17:14
quelle