Wann NICHT das Abhängigkeitsinversionsprinzip anwenden?


43

Ich versuche gerade, SOLID herauszufinden. Das Prinzip der Abhängigkeitsinversion bedeutet also, dass zwei Klassen nicht direkt, sondern über Schnittstellen kommunizieren sollten. Beispiel: Wenn class Aeine Methode vorhanden ist, die einen Zeiger auf ein Objekt vom Typ erwartet class B, sollte diese Methode tatsächlich ein Objekt vom Typ erwarten abstract base class of B. Dies hilft auch beim Öffnen / Schließen.

Vorausgesetzt, ich habe das richtig verstanden, wäre meine Frage, ob es eine gute Praxis ist, dies auf alle Klasseninteraktionen anzuwenden, oder ob ich versuchen sollte, in Ebenen zu denken ?

Der Grund, warum ich skeptisch bin, ist, dass wir für die Befolgung dieses Prinzips einen gewissen Preis zahlen. Angenommen, ich muss die Funktion implementieren Z. Nach der Analyse komme ich zu dem Schluss, dass das Merkmal Zaus Funktionalität besteht A, Bund C. Ich erstelle eine Fassade Klasse Z, dass durch Schnittstellen verwendet Klassen A, Bund C. Ich beginne die Codierung der Umsetzung und an einem gewissen Punkt , den ich erkennen , dass Aufgabe Zder Funktionalität tatsächlich besteht A, Bund D. Jetzt muss ich das CInterface, den CKlassenprototyp, ausrangieren und separate DInterfaces und Klassen schreiben . Ohne Schnittstellen hätte nur die Klasse ersetzt werden müssen.

Mit anderen Worten, um etwas zu ändern, muss ich 1. den Aufrufer 2. die Schnittstelle 3. die Deklaration 4. die Implementierung ändern. In einer Python direkt gekoppelten Implementierung müsste ich nur die Implementierung ändern .


13
Die Abhängigkeitsumkehrung ist einfach eine Technik, daher sollte sie nur bei Bedarf angewendet werden. Wenn Sie sie also überall anwenden, fällt wie in jeder anderen Situation Müll an -spezifische Technik.
Frank Hileman

Vereinfacht ausgedrückt hängt die Anwendung einiger Prinzipien des Software-Designs davon ab, dass bei sich ändernden Anforderungen eine unbarmherzige Umgestaltung möglich ist. Von diesen wird angenommen , dass der Schnittstellenteil die vertraglichen Invarianten des Entwurfs am besten erfasst, während erwartet wird, dass der Quellcode (Implementierung) häufigere Änderungen toleriert.
Rwong

@rwong Eine Schnittstelle erfasst Vertragsinvarianten nur, wenn Sie eine Sprache verwenden, die Vertragsinvarianten unterstützt. In gängigen Sprachen (Java, C #) besteht eine Schnittstelle lediglich aus einer Reihe von API-Signaturen. Das Hinzufügen überflüssiger Schnittstellen beeinträchtigt nur das Design.
Frank Hileman

Ich würde sagen, Sie haben es falsch verstanden. Bei DIP geht es darum, Abhängigkeiten bei der Kompilierung von einer "High-Level" -Komponente zu einer "Low-Level" -Komponente zu vermeiden, um die Wiederverwendung der High-Level-Komponente in anderen Kontexten zu ermöglichen, in denen Sie eine andere Implementierung für die Low-Ebene verwenden würden -Level-Komponente; Dazu wird auf hoher Ebene ein abstrakter Typ erstellt, der von den Komponenten auf niedriger Ebene implementiert wird. Daher hängen sowohl High- als auch Low-Level-Komponenten von dieser Abstraktion ab. Letztendlich kommunizieren High- und Low-Level-Komponenten über eine Schnittstelle, aber dies ist nicht die Essenz von DIP.
Rogério

Antworten:


87

In vielen Cartoons oder anderen Medien werden die Kräfte von Gut und Böse oft durch einen Engel und einen Dämon dargestellt, die auf den Schultern des Charakters sitzen. In unserer Geschichte hier haben wir anstelle von Gut und Böse SOLID auf einer Schulter und YAGNI (Du wirst es nicht brauchen!) Sitzt auf der anderen.

Optimale SOLID-Prinzipien eignen sich am besten für riesige, komplexe, ultrakonfigurierbare Unternehmenssysteme. Für kleinere oder spezifischere Systeme ist es nicht angebracht, alles lächerlich flexibel zu gestalten, da sich die Zeit, die Sie mit dem Abstrahieren von Dingen verbringen, nicht als Vorteil erweist.

Das Übergeben von Schnittstellen anstelle von konkreten Klassen bedeutet beispielsweise manchmal, dass Sie das Lesen aus einer Datei einfach gegen einen Netzwerkstrom austauschen können. Für eine große Anzahl von Softwareprojekten ist diese Flexibilität jedoch nicht immer erforderlich. Sie können auch konkrete Dateiklassen absolvieren und es als Tag bezeichnen und Ihre Gehirnzellen schonen.

Ein Teil der Kunst der Softwareentwicklung besteht darin, ein gutes Gefühl dafür zu haben, was sich im Laufe der Zeit wahrscheinlich ändern wird und was nicht. Verwenden Sie für die Dinge, die sich wahrscheinlich ändern, die Schnittstellen und andere SOLID-Konzepte. Verwenden Sie YAGNI und übergeben Sie einfach konkrete Typen, vergessen Sie die Factory-Klassen, vergessen Sie das gesamte Anschließen und Konfigurieren der Laufzeit usw. und vergessen Sie viele der SOLID-Abstraktionen. Meiner Erfahrung nach hat sich der YAGNI-Ansatz weitaus häufiger als unkorrekt erwiesen.


19
Meine erste Einführung in SOLID erfolgte vor ungefähr 15 Jahren auf einem neuen System, das wir gerade bauten. Wir alle tranken den koolen Helfer. Wenn irgendjemand etwas erwähnte, das nach YAGNI klang , waren wir wie "Pfffft ... plebeian". Ich hatte die Ehre (Horror?) Zu beobachten, wie sich dieses System im Laufe des nächsten Jahrzehnts weiterentwickelte. Es wurde zu einem unhandlichen Durcheinander, das niemand verstehen konnte, nicht einmal wir Gründer. Architekten lieben SOLID. Menschen, die ihren Lebensunterhalt tatsächlich verdienen, lieben YAGNI. Weder ist perfekt, aber YAGNI ist perfekter und sollte Ihre Standardeinstellung sein, wenn Sie nicht wissen, was Sie tun. :-)
Calphool

11
@NWard Ja, das haben wir bei einem Projekt gemacht. Ging verrückt damit. Jetzt können unsere Tests nicht mehr gelesen oder gewartet werden, was zum Teil auf das Verspotten zurückzuführen ist. Darüber hinaus ist es aufgrund der Abhängigkeitsinjektion schwierig, durch den Code zu navigieren, wenn Sie versuchen, etwas herauszufinden. SOLID ist keine Wunderwaffe. YAGNI ist keine Wunderwaffe. Automatisierte Tests sind keine Wunderwaffe. Nichts kann Sie davon abhalten, hart darüber nachzudenken, was Sie tun und Entscheidungen darüber zu treffen, ob dies Ihrer Arbeit oder der Arbeit eines anderen hilft oder sie behindert.
jpmc26

22
Viel anti-SOLID Stimmung hier. SOLID und YAGNI sind nicht zwei Enden eines Spektrums. Sie sind wie die X- und Y-Koordinaten in einem Diagramm. Ein gutes System hat sehr wenig überflüssigen Code (YAGNI) UND folgt den SOLID-Prinzipien.
Stephen

31
Meh, (a) ich bin nicht einverstanden, dass SOLID = enterprisey und (b) der springende Punkt von SOLID ist, dass wir dazu neigen, äußerst schlechte Prognostiker dessen zu sein, was benötigt wird. Ich muss @Stephen hier zustimmen. YAGNI sagt, dass wir nicht versuchen sollten, zukünftige Anforderungen zu antizipieren, die heute nicht klar formuliert sind. SOLID sagt, dass wir erwarten sollten, dass sich das Design im Laufe der Zeit weiterentwickelt und bestimmte einfache Techniken anwendet, um es zu vereinfachen. Sie schließen sich nicht gegenseitig aus. beides sind techniken zur anpassung an veränderte anforderungen. Die wirklichen Probleme treten auf, wenn Sie versuchen, für unklare oder sehr entfernte Anforderungen zu entwerfen.
Aaronaught

8
"Sie können das Lesen einer Datei einfach gegen einen Netzwerk-Stream austauschen" - dies ist ein gutes Beispiel, bei dem eine stark vereinfachte Beschreibung von DI die Menschen in die Irre führt. Die Leute denken manchmal (im Endeffekt), "diese Methode wird a verwenden File, und stattdessen wird es einen IFileJob erfordern, der erledigt wird". Dann können sie einen Netzwerk-Stream nicht einfach ersetzen, weil sie die Schnittstelle überfordert haben, und es gibt Vorgänge, IFiledie die Methode nicht einmal verwendet, die nicht für Sockets gelten, sodass ein Socket nicht implementiert werden kann IFile. Eines der Dinge, für die DI kein Königsweg ist, ist die Erfindung der richtigen Abstraktionen (Schnittstellen) :-)
Steve Jessop

11

Mit den Worten des Laien:

Das Anwenden des DIP ist einfach und macht Spaß . Das Design nicht gleich beim ersten Versuch richtig zu machen, ist nicht genug, um das DIP ganz aufzugeben.

  • In der Regel unterstützen IDEs Sie bei dieser Art von Refactoring. In einigen Fällen können Sie sogar die Schnittstelle aus einer bereits implementierten Klasse extrahieren
  • Es ist fast unmöglich, das Design gleich beim ersten Mal richtig zu machen
  • Der normale Workflow beinhaltet das Ändern und Überdenken der Schnittstellen in den ersten Phasen der Entwicklung
  • Mit fortschreitender Entwicklung wird es reifer und Sie haben weniger Gründe, die Schnittstellen zu ändern
  • In einem fortgeschrittenen Stadium werden die Schnittstellen (das Design) ausgereift sein und sich kaum ändern
  • Ab diesem Moment profitieren Sie von den Vorteilen, da Ihre App skalierbar ist.

Auf der anderen Seite kann die Programmierung mit Schnittstellen und OOD die Freude an dem manchmal veralteten Handwerk der Programmierung zurückbringen.

Einige Leute sagen, es fügt Komplexität hinzu, aber ich denke, dass die Opossite wahr ist. Auch für kleine Projekte. Es erleichtert das Testen / Verspotten. Es führt dazu, dass Ihr Code weniger caseAnweisungen enthält oder verschachtelt ist ifs. Es reduziert die zyklomatische Komplexität und bringt Sie zum Nachdenken. Dadurch wird die Programmierung realistischer in Bezug auf Design und Fertigung.


5
Ich weiß nicht, welche Sprachen oder IDEs vom OP verwendet werden, aber in VS 2013 ist es lächerlich einfach, mit Schnittstellen zu arbeiten, Schnittstellen zu extrahieren und zu implementieren, und entscheidend, wenn TDD verwendet wird. Für die Entwicklung nach diesen Grundsätzen entsteht kein zusätzlicher Entwicklungsaufwand.
Stephenbayer

Warum handelt es sich bei dieser Antwort um DI, wenn es sich um DIP handelt? DIP ist ein Konzept aus den 1990er Jahren, während DI aus dem Jahr 2004 stammt. Sie sind sehr unterschiedlich.
Rogério

1
(Mein vorheriger Kommentar war für eine andere Antwort gedacht; ignorieren Sie ihn.) "Programmieren auf Schnittstellen" ist viel allgemeiner als DIP, aber es geht nicht darum, dass jede Klasse eine separate Schnittstelle implementiert. Und es macht "Testen / Verspotten" nur dann einfacher, wenn die Test- / Verspottungswerkzeuge unter schwerwiegenden Einschränkungen leiden.
Rogério

@ Rogério Normalerweise implementiert nicht jede Klasse bei der Verwendung von DI eine separate Schnittstelle. Eine Schnittstelle, die von mehreren Klassen implementiert wird, ist üblich.
Tulains Córdova

@ Rogério Ich habe meine Antwort korrigiert, jedes Mal, wenn ich DI erwähnte, meinte ich DIP.
Tulains Córdova

9

Verwenden Sie die Abhängigkeitsinversion dort, wo es Sinn macht.

Ein extremes Gegenbeispiel ist die in vielen Sprachen enthaltene Klasse "string". Es stellt ein primitives Konzept dar, im Wesentlichen ein Array von Zeichen. Angenommen, Sie könnten diese Kernklasse ändern, ist es nicht sinnvoll, DI hier zu verwenden, da Sie den internen Zustand niemals durch etwas anderes ersetzen müssen.

Wenn Sie eine Gruppe von Objekten haben, die intern in einem Modul verwendet werden und die anderen Modulen nicht ausgesetzt oder nirgendwo wiederverwendet werden, lohnt es sich wahrscheinlich nicht, DI zu verwenden.

Es gibt zwei Stellen, an denen DI meiner Meinung nach automatisch verwendet werden sollte:

  1. In den Modulen konzipiert für die Erweiterung. Wenn der gesamte Zweck eines Moduls darin besteht, es zu erweitern und das Verhalten zu ändern, ist es vollkommen sinnvoll, DI von Anfang an einzubacken.

  2. In Modulen, die Sie für den Zweck der Wiederverwendung von Code überarbeiten. Vielleicht haben Sie eine Klasse programmiert, um etwas zu tun, und später festgestellt, dass Sie diesen Code mit einem Refactor an anderer Stelle einsetzen können, und dies ist unbedingt erforderlich . Das ist ein großartiger Kandidat für DI und andere Erweiterungsänderungen.

Die Schlüssel werden dort verwendet, wo sie benötigt werden, da dies zusätzliche Komplexität mit sich bringt und Sie sicherstellen, dass Sie diesen Bedarf entweder anhand der technischen Anforderungen (Punkt 1) oder der Überprüfung des quantitativen Codes (Punkt 2) messen können .

DI ist ein großartiges Tool, aber genau wie jedes * Tool kann es überbeansprucht oder missbraucht werden.

* Ausnahme von der obigen Regel: Eine Säbelsäge ist das perfekte Werkzeug für jede Arbeit. Wenn es Ihr Problem nicht behebt, wird es entfernt. Permanent.


3
Was ist, wenn "Ihr Problem" ein Loch in der Wand ist? Eine Säge würde es nicht entfernen; es würde es noch schlimmer machen. ;)
Mason Wheeler

3
@ MasonWheeler mit einer leistungsstarken und unterhaltsamen Säge, "Loch in der Wand" könnte sich in "Tür" verwandeln, was eine nützliche

1
Kannst du nicht mit einer Säge einen Patch für das Loch machen?
JeffO

Obwohl die Verwendung eines nicht vom Benutzer erweiterbaren StringTyps einige Vorteile bietet , sind in vielen Fällen alternative Darstellungen hilfreich, wenn der Typ über eine Reihe guter virtueller Operationen verfügt (z. B. Kopieren eines Teilstrings in einen bestimmten Teil von a short[], Melden Sie, ob a Teilzeichenfolge enthält nur ASCII oder enthält möglicherweise nur ASCII. Versuchen Sie, eine Teilzeichenfolge, von der angenommen wird, dass sie nur ASCII enthält, in einen bestimmten Teil von a zu kopieren byte[].
Supercat

1
Warum handelt es sich bei dieser Antwort um DI, wenn es sich um DIP handelt? DIP ist ein Konzept aus den 1990er Jahren, während DI aus dem Jahr 2004 stammt. Sie sind sehr unterschiedlich.
Rogério

5

Es scheint mir, dass die ursprüngliche Frage einen Teil des DIP-Punktes verfehlt.

Der Grund, warum ich skeptisch bin, ist, dass wir für die Befolgung dieses Prinzips einen gewissen Preis zahlen. Angenommen, ich muss Merkmal Z implementieren. Nach der Analyse komme ich zu dem Schluss, dass Merkmal Z aus den Funktionen A, B und C besteht. Ich erstelle eine Fassadenklasse Z, die über Schnittstellen die Klassen A, B und C verwendet Implementierung und irgendwann merke ich, dass Aufgabe Z tatsächlich aus Funktionalität A, B und D besteht. Jetzt muss ich die C-Schnittstelle, den Prototyp der C-Klasse, verschrotten und separate D-Schnittstelle und Klasse schreiben. Ohne Schnittstellen müsste nur die Klasse wave ersetzt werden.

Um das DIP wirklich nutzen zu können, müssen Sie zuerst die Klasse Z erstellen und die Funktionalität der Klassen A, B und C aufrufen (die noch nicht entwickelt wurden). Auf diese Weise erhalten Sie die API für die Klassen A, B und C. Anschließend erstellen Sie die Klassen A, B und C und geben die Details ein. Sie sollten effektiv die Abstraktionen erstellen, die Sie benötigen, während Sie die Klasse Z erstellen, basierend auf den Anforderungen der Klasse Z. Sie können sogar Tests um die Klasse Z schreiben, bevor die Klassen A, B oder C überhaupt geschrieben werden.

Denken Sie daran, dass das DIP besagt, dass "High-Level-Module nicht von Low-Level-Modulen abhängen sollten. Beide sollten von Abstraktionen abhängen."

Wenn Sie herausgefunden haben, was die Klasse Z braucht und wie sie bekommen will, was sie braucht, können Sie die Details eintragen. Sicher, manchmal müssen Änderungen an Klasse Z vorgenommen werden, aber in 99% der Fälle ist dies nicht der Fall.

Es wird nie eine Klasse D geben, weil Sie herausgefunden haben, dass Z A, B und C benötigt, bevor sie geschrieben wurden. Eine Änderung der Anforderungen ist eine ganz andere Geschichte.


5

Die kurze Antwort lautet "fast nie", aber es gibt tatsächlich einige Stellen, an denen das DIP keinen Sinn ergibt:

  1. Fabriken oder Bauunternehmen, deren Aufgabe es ist, Objekte zu erstellen. Dies sind im Wesentlichen die "Blattknoten" in einem System, das die IoC vollständig umfasst. Irgendwann muss etwas tatsächlich Ihre Objekte erstellen und kann nicht von etwas anderem abhängen, um dies zu tun. In vielen Sprachen kann ein IoC-Container dies für Sie tun, aber manchmal müssen Sie es auf die altmodische Weise tun.

  2. Implementierung von Datenstrukturen und Algorithmen. Im Allgemeinen hängen in diesen Fällen die hervorstechenden Merkmale, für die Sie optimieren (z. B. Laufzeit und asymptotische Komplexität), von den verwendeten Datentypen ab. Wenn Sie eine Hash-Tabelle implementieren, müssen Sie wirklich wissen, dass Sie mit einem Array zum Speichern arbeiten, nicht mit einer verknüpften Liste, und nur die Tabelle selbst weiß, wie die Arrays richtig zugeordnet werden. Sie möchten auch kein veränderliches Array übergeben und den Aufrufer dazu bringen, Ihre Hash-Tabelle zu durchbrechen, indem er mit ihrem Inhalt spielt.

  3. Domänenmodellklassen . Diese implementieren Ihre Geschäftslogik, und (in den meisten Fällen) ist es nur sinnvoll, eine Implementierung zu haben, da Sie (in den meisten Fällen) die Software nur für ein Unternehmen entwickeln. Obwohl einige Domänenmodellklassen möglicherweise mit anderen Domänenmodellklassen erstellt werden, wird dies im Allgemeinen von Fall zu Fall erfolgen. Da Domänenmodellobjekte keine Funktionalität enthalten, die sinnvoll nachgeahmt werden kann, bietet der DIP keinen Vorteil für die Testbarkeit oder Wartbarkeit.

  4. Alle Objekte, die als externe API bereitgestellt werden und andere Objekte erstellen müssen, deren Implementierungsdetails Sie nicht öffentlich zugänglich machen möchten. Dies fällt unter die allgemeine Kategorie "Bibliotheksdesign unterscheidet sich vom Anwendungsdesign". Eine Bibliothek oder ein Framework kann DI intern liberal nutzen, muss aber irgendwann tatsächlich arbeiten, da es sich sonst nicht um eine sehr nützliche Bibliothek handelt. Angenommen, Sie entwickeln eine Netzwerkbibliothek. Sie möchten wirklich nicht, dass der Verbraucher eine eigene Implementierung eines Sockets bereitstellen kann. Sie können eine Abstraktion eines Sockets intern verwenden, aber die API, die Sie für Aufrufer bereitstellen, erstellt eigene Sockets.

  5. Unit-Tests und Test-Doppel. Fälschungen und Stummel sollen eine Sache tun und es einfach tun. Wenn Sie eine Fälschung haben, die komplex genug ist, um sich Gedanken darüber zu machen, ob Sie eine Abhängigkeitsinjektion durchführen sollen oder nicht, ist sie wahrscheinlich zu komplex (vielleicht, weil sie eine Schnittstelle implementiert, die auch viel zu komplex ist).

Es könnte mehr geben; das sind die, die ich ziemlich häufig sehe .


Wie wäre es mit "immer wenn Sie in einer dynamischen Sprache arbeiten"?
Kevin,

Nein? Ich arbeite viel in JavaScript und es gilt dort immer noch genauso gut. Das "O" und "I" in SOLID kann jedoch etwas unscharf werden.
Aaronaught

Huh ... Ich finde, dass Pythons erstklassige Typen in Kombination mit der Eingabe von Enten etwas weniger notwendig sind.
Kevin

Das DIP hat absolut nichts mit dem Typsystem zu tun. Und inwiefern sind "erstklassige Typen" einzigartig für Python? Wenn Sie etwas isoliert testen möchten, müssen Sie die Abhängigkeiten durch Test-Doubles ersetzen. Diese Test-Doubles können alternative Implementierungen einer Schnittstelle (in statisch typisierten Sprachen) oder anonyme Objekte oder alternative Typen sein, die zufällig dieselben Methoden / Funktionen haben (Enten-Typisierung). In beiden Fällen benötigen Sie noch eine Möglichkeit, eine Instanz tatsächlich zu ersetzen.
Aaronaught

1
@ Kevin Python war kaum die erste Sprache, die entweder dynamische Typisierung oder lose Mocks besaß. Es ist auch völlig irrelevant. Die Frage ist nicht, welcher Objekttyp vorliegt, sondern wie / wo das Objekt erstellt wird. Wenn ein Objekt seine eigenen Abhängigkeiten erstellt, müssen Sie die Details der Implementierung einer Unit testen, indem Sie schreckliche Dinge tun, z. Das Vergessen von Test, Mischverhalten und Objektkonstruktion führt einfach zu einer engen Kopplung. Duck Typing löst keines dieser Probleme.
Aaronaught

2

Einige Anzeichen, dass Sie DIP auf einer zu kleinen Ebene anwenden, auf der es keinen Wert liefert:

  • Sie haben ein C / CImpl- oder IC / C-Paar mit nur einer einzigen Implementierung dieser Schnittstelle
  • Die Signaturen in Ihrer Benutzeroberfläche und Implementierung stimmen eins zu eins überein (Verstoß gegen das DRY-Prinzip).
  • Sie ändern häufig C und CImpl gleichzeitig.
  • C ist projektintern und wird außerhalb Ihres Projekts nicht als Bibliothek freigegeben.
  • Sie sind frustriert, wenn F3 in Eclipse / F12 in Visual Studio Sie zur Benutzeroberfläche anstelle der eigentlichen Klasse führt

Wenn Sie dies sehen, ist es möglicherweise besser, wenn Sie Z C direkt aufrufen und die Schnittstelle überspringen.

Ich denke auch nicht, dass Methodendekoration durch ein Abhängigkeitsinjektions- / dynamisches Proxy-Framework (Spring, Java EE) genauso wie echtes SOLID DIP funktioniert. Nach Meinung der Java EE-Community ist es eine Verbesserung, dass Sie keine Foo / FooImpl-Paare mehr benötigen, wie Sie es gewohnt sind ( Referenz ). Im Gegensatz dazu unterstützt Python die Funktionsdekoration als erstklassiges Sprachmerkmal.

Siehe auch diesen Blog-Beitrag .


0

Wenn Sie Ihre Abhängigkeiten immer umkehren, stehen alle Abhängigkeiten auf dem Kopf. Das heißt, wenn Sie mit chaotischem Code mit einem Knoten von Abhängigkeiten angefangen haben, ist das, was Sie immer noch (wirklich) haben, nur invertiert. An dieser Stelle tritt das Problem auf, dass bei jeder Änderung an einer Implementierung auch die Benutzeroberfläche geändert werden muss.

Der Punkt der Abhängigkeitsinversion ist, dass Sie die Abhängigkeiten, die die Dinge verwickeln, selektiv invertieren. Diejenigen, die von A nach B nach C gehen sollten, tun dies immer noch. Es sind diejenigen, die von C nach A gehen, die jetzt von A nach C gehen.

Das Ergebnis sollte ein zyklenfreies Abhängigkeitsdiagramm sein - eine DAG. Es gibt verschiedene Tools , die diese Eigenschaft prüfen und das Diagramm zeichnen.

Eine ausführlichere Erklärung finden Sie in diesem Artikel :

Das Wesentliche bei der korrekten Anwendung des Abhängigkeitsinversionsprinzips ist Folgendes:

Teilen Sie den Code / Service /…, von dem Sie abhängig sind, in eine Schnittstelle und eine Implementierung auf. Die Schnittstelle strukturiert die Abhängigkeit im Jargon des verwendeten Codes um, die Implementierung implementiert sie in Bezug auf die zugrunde liegenden Techniken.

Die Implementierung bleibt dort, wo sie ist. Die Benutzeroberfläche hat jetzt eine andere Funktion (und verwendet eine andere Jargon / Sprache) und beschreibt etwas, was der verwendende Code tun kann. Verschiebe es in dieses Paket. Indem die Schnittstelle und die Implementierung nicht in dasselbe Paket gestellt werden, wird die (Richtung der) Abhängigkeit von Benutzer → Implementierung zu Implementierung → Benutzer umgekehrt.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.