Frage

Ich verwende StructureMap seit kurzem und habe die Erfahrung sehr genossen.Ich kann mir jedoch vorstellen, dass man sich leicht davon hinreißen lässt, alles mit Schnittstellen zu versehen, und am Ende Klassen erhält, die eine Unmenge an Schnittstellen in ihre Konstruktoren aufnehmen.Auch wenn das eigentlich kein großes Problem darstellt, wenn Sie ein Dependency-Injection-Framework verwenden, hat man dennoch den Eindruck, dass es bestimmte Eigenschaften gibt, die eigentlich nicht herausgebunden werden müssen, nur um sie anzubinden.

Wo ziehen Sie die Grenze zwischen der Schnittstelle und dem bloßen Hinzufügen einer Eigenschaft zur Klasse?

War es hilfreich?

Lösung

Denken Sie über Ihr Design nach.Mit DI können Sie die Funktionsweise Ihres Codes durch Konfigurationsänderungen ändern.Außerdem können Sie Abhängigkeiten zwischen Klassen aufheben, sodass Sie Objekte einfacher isolieren und testen können.Sie müssen feststellen, wo dies sinnvoll ist und wo nicht.Es gibt keine eindeutige Antwort.

Eine gute Faustregel lautet: Wenn das Testen zu schwierig ist, treten Probleme mit Einzelverantwortung und statischen Abhängigkeiten auf.Isolieren Sie Code, der eine einzelne Funktion ausführt, in einer Klasse und unterbrechen Sie diese statische Abhängigkeit, indem Sie eine Schnittstelle extrahieren und ein DI-Framework verwenden, um zur Laufzeit die richtige Instanz einzufügen.Dadurch wird es einfacher, die beiden Teile separat zu testen.

Andere Tipps

Das Hauptproblem bei der Abhängigkeitsinjektion besteht darin, dass sie zwar den Anschein einer lose gekoppelten Architektur erweckt, dies aber in Wirklichkeit nicht der Fall ist.

Was Sie eigentlich tun, ist, diese Kopplung von der Kompilierungszeit zur Laufzeit zu verschieben, aber wenn Klasse A trotzdem eine Schnittstelle B benötigt, um zu funktionieren, muss immer noch eine Instanz einer Klasse bereitgestellt werden, die Schnittstelle B implementiert.

Die Abhängigkeitsinjektion sollte nur für die Teile der Anwendung verwendet werden, die dynamisch geändert werden müssen, ohne den Basiscode neu zu kompilieren.

Verwendungsmöglichkeiten, die ich für ein Inversion of Control-Muster als nützlich erachtet habe:

  • Eine Plugin-Architektur.Indem Sie also die richtigen Einstiegspunkte festlegen, können Sie den Vertrag für die zu erbringende Dienstleistung definieren.
  • Workflow-ähnliche Architektur.Hier können Sie mehrere Komponenten verbinden, indem Sie den Ausgang einer Komponente dynamisch mit dem Eingang einer anderen verbinden.
  • Anwendung pro Client.Nehmen wir an, Sie haben verschiedene Kunden, die für eine Reihe von „Funktionen“ Ihres Projekts bezahlen.Mithilfe der Abhängigkeitsinjektion können Sie ganz einfach nur die Kernkomponenten und einige „zusätzliche“ Komponenten bereitstellen, die genau die Funktionen bereitstellen, für die der Kunde bezahlt hat.
  • Übersetzung.Obwohl dies normalerweise nicht zu Übersetzungszwecken geschieht, können Sie je nach Bedarf der Anwendung unterschiedliche Sprachdateien „einschleusen“.Dazu gehören je nach Bedarf RTL- oder LTR-Benutzeroberflächen.

Die Abhängigkeitsinjektion sollte nur für die Teile der Anwendung verwendet werden, die dynamisch geändert werden müssen, ohne den Basiscode neu zu kompilieren

DI sollte verwendet werden, um Ihren Code von externen Ressourcen (Datenbanken, Webservices, XML-Dateien, Plugin-Architektur) zu isolieren.Der Zeitaufwand, der zum Testen Ihrer Logik im Code erforderlich wäre, wäre in vielen Unternehmen nahezu unerschwinglich, wenn Sie Komponenten testen, die von einer Datenbank ABHÄNGEN.

In den meisten Anwendungen wird sich die Datenbank nicht dynamisch ändern (obwohl dies der Fall sein könnte), aber im Allgemeinen empfiehlt es sich fast immer, Ihre Anwendung NICHT an eine bestimmte externe Ressource zu binden.Der Aufwand für die Änderung von Ressourcen sollte gering sein (Datenzugriffsklassen sollten selten eine zyklomatische Komplexität über eins in ihren Methoden aufweisen).

Was meinst du mit „einfach eine Eigenschaft zu einer Klasse hinzufügen“?

Meine Faustregel ist, die Klasseneinheit testbar zu machen.Wenn Ihre Klasse auf den Implementierungsdetails einer anderen Klasse basiert, muss diese so weit umgestaltet/abstrahiert werden, dass die Klassen isoliert getestet werden können.

BEARBEITEN:Sie erwähnen eine Menge Schnittstellen im Konstruktor.Ich würde stattdessen die Verwendung von Setter/Getter empfehlen.Ich finde, dass es die Wartung auf lange Sicht viel einfacher macht.

Ich mache es nur, wenn es bei der Trennung von Bedenken hilft.

Wie vielleicht projektübergreifend würde ich eine Schnittstelle für Implementierer in einem meiner Bibliotheksprojekte bereitstellen und das implementierende Projekt würde jede gewünschte spezifische Implementierung einfügen.

Aber das war es schon...In allen anderen Fällen würde es das System nur unnötig komplex machen

Trotz aller Fakten und Prozesse auf der Welt.Jede Entscheidung läuft auf ein Urteil hinaus. Ich habe vergessen, wo ich das gelesen habe
Ich denke, es ist eher eine Erfahrungs-/Flugzeitaufforderung.Wenn Sie die Abhängigkeit als Kandidatenobjekt betrachten, das möglicherweise in naher Zukunft ersetzt wird, verwenden Sie grundsätzlich die Abhängigkeitsinjektion.Wenn ich „KlasseA und ihre Abhängigkeiten“ als einen Block zum Ersetzen betrachte, werde ich DI wahrscheinlich nicht für die Deps von A verwenden.

Der größte Vorteil besteht darin, dass es Ihnen hilft, die Architektur Ihrer Anwendung zu verstehen oder sogar aufzudecken.Sie können sehr deutlich sehen, wie Ihre Abhängigkeitsketten funktionieren, und Änderungen an einzelnen Teilen vornehmen, ohne dass Sie Dinge ändern müssen, die nichts miteinander zu tun haben.Am Ende erhalten Sie eine lose gekoppelte Anwendung.Dies führt Sie zu einem besseren Design und Sie werden überrascht sein, wenn Sie weitere Verbesserungen vornehmen können, da Ihr Design Ihnen dabei hilft, den Code auch in Zukunft zu trennen und zu organisieren.Es kann auch Unit-Tests erleichtern, da Sie jetzt eine natürliche Möglichkeit haben, Implementierungen bestimmter Schnittstellen zu ersetzen.

Es gibt einige Anwendungen, die einfach wegwerfbar sind, aber im Zweifelsfall würde ich die Schnittstellen erstellen.Mit etwas Übung stellt es keine große Belastung dar.

Ein weiterer Gegenstand, mit dem ich kämpfe, ist Wo soll ich die Abhängigkeitsinjektion verwenden? Woher kommt Ihre Abhängigkeit von StructureMap?Nur in der Startanwendung?Bedeutet das, dass alle Implementierungen von der obersten bis zur untersten Schicht weitergegeben werden müssen?

Ich verwende Castle Windsor/Microkernel, ich habe keine Erfahrung mit etwas anderem, aber es gefällt mir sehr gut.

Wie entscheiden Sie, was Sie injizieren?Bisher hat mir folgende Faustregel gute Dienste geleistet:Wenn die Klasse so einfach ist, dass keine Komponententests erforderlich sind, können Sie sie gerne in der Klasse instanziieren. Andernfalls möchten Sie wahrscheinlich eine Abhängigkeit über den Konstruktor haben.

Was die Frage angeht, ob Sie eine Schnittstelle erstellen oder nur Ihre Methoden und Eigenschaften virtuell machen sollten, sollten Sie meiner Meinung nach den Schnittstellenweg wählen, entweder wenn Sie a) feststellen, dass die Klasse in einer anderen Anwendung ein gewisses Maß an Wiederverwendbarkeit aufweist (z. B.ein Logger) oder b) wenn die Klasse aufgrund der Menge an Konstruktorparametern oder weil der Konstruktor eine erhebliche Menge an Logik enthält, ansonsten schwer zu verspotten ist.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top