Angenommen, wir haben ein Softwaremodul A, das eine Funktion F implementiert. Ein anderes Modul B implementiert die gleiche Funktion wie F '.
Es gibt verschiedene Möglichkeiten, um den doppelten Code zu entfernen:
- A benutze F 'aus B.
- Lassen Sie B F von A verwenden.
- Setzen Sie F in sein eigenes Modul C ein und lassen Sie A und B es verwenden.
Alle diese Optionen erzeugen zusätzliche Abhängigkeiten zwischen Modulen. Sie wenden das DRY-Prinzip auf Kosten der Erhöhung der Kopplung an.
Soweit ich sehen kann, wird die Kopplung beim Auftragen von DRY immer erhöht oder zumindest auf ein höheres Niveau verschoben. Es scheint einen Konflikt zwischen zwei der grundlegendsten Prinzipien des Software-Designs zu geben.
(Eigentlich finde ich es nicht verwunderlich, dass es solche Konflikte gibt. Dies ist wahrscheinlich der Grund, warum gutes Software-Design so schwierig ist. Ich finde es verwunderlich, dass diese Konflikte normalerweise nicht in einleitenden Texten behandelt werden.)
Edit (zur Verdeutlichung): Ich gehe davon aus, dass die Gleichheit von F und F 'nicht nur ein Zufall ist. Wenn F modifiziert werden muss, muss F 'wahrscheinlich auf die gleiche Weise modifiziert werden.
quelle
Antworten:
Warum tun sie das? Sie verringern jedoch die Kopplung zwischen den Leitungen. Was Sie bekommen, ist die Kraft, die Kupplung zu ändern. Kopplung gibt es in vielen Formen. Das Extrahieren von Code erhöht die Indirektion und Abstraktion. Das kann gut oder schlecht sein. Die Nummer eins, die entscheidet, welche Sie erhalten, ist der Name, den Sie dafür verwenden. Wenn ein Blick auf den Namen mich überrascht, wenn ich hineinschaue, dann haben Sie niemandem einen Gefallen getan.
Folgen Sie DRY auch nicht im luftleeren Raum. Wenn Sie die Vervielfältigung beenden, müssen Sie vorhersagen, dass sich diese beiden Verwendungszwecke des Codes gemeinsam ändern werden. Wenn sie sich wahrscheinlich unabhängig voneinander ändern, haben Sie Verwirrung und zusätzliche Arbeit mit geringem Nutzen verursacht. Aber ein wirklich guter Name kann das schmackhafter machen. Wenn Sie nur an einen schlechten Ruf denken können, hören Sie jetzt einfach auf.
Eine Kopplung besteht immer, es sei denn, Ihr System ist so isoliert, dass niemand jemals weiß, ob es funktioniert. Das Refactoring der Kopplung ist also ein Spiel, bei dem Sie Ihr Gift auswählen müssen. Die Einhaltung von DRY kann sich auszahlen, indem die entstehende Kopplung minimiert wird, indem an vielen Stellen dieselbe Konstruktionsentscheidung wiederholt zum Ausdruck gebracht wird, bis eine Änderung sehr schwierig ist. Aber DRY kann es unmöglich machen, Ihren Code zu verstehen. Der beste Weg, diese Situation zu retten, besteht darin, einen wirklich guten Namen zu finden. Wenn Ihnen kein guter Name einfällt, können Sie hoffentlich bedeutungslose Namen vermeiden
quelle
Es gibt Möglichkeiten, explizite Abhängigkeiten aufzuheben. Eine beliebte Methode ist das Einfügen von Abhängigkeiten in die Laufzeit. Auf diese Weise erhalten Sie DRY, entfernen die Kupplung auf Kosten der statischen Sicherheit. Es ist heutzutage so beliebt, dass die Leute es nicht einmal verstehen, dass das ein Kompromiss ist. Beispielsweise stellen Anwendungscontainer routinemäßig ein Abhängigkeitsmanagement bereit, das die Software erheblich kompliziert, indem es die Komplexität verbirgt. Sogar die einfache Injektion alter Konstrukteure kann einige Verträge aufgrund fehlender Typsysteme nicht garantieren.
Um den Titel zu beantworten - ja, es ist möglich, aber auf die Folgen des Laufzeitversands vorbereitet zu sein.
Auf diese Weise ist die einzige Art von Abhängigkeiten, die Sie haben würden, D, abhängig von den anderen Modulen.
Oder registrieren Sie C im Anwendungscontainer mit integrierter Abhängigkeitsinjektion und genießen Sie die Möglichkeit, automatisch langsam wachsende Laufzeitklassen-Ladeschleifen und Deadlocks zu verdrahten.
quelle
Ich bin mir nicht sicher, ob eine Antwort ohne weiteren Kontext sinnvoll ist.
Kommt
A
schon drauf anB
oder umgekehrt? - In diesem Fall könnten wir eine offensichtliche Wahl für unser Zuhause habenF
.Haben
A
undB
teilen Sie bereits gemeinsame Abhängigkeiten, für die ein gutes Zuhause sein könnteF
?Wie groß / komplex ist
F
? Was hängt nochF
davon ab?Werden Module
A
undB
im selben Projekt verwendet?Wird
A
und wird esB
trotzdem eine gemeinsame Abhängigkeit geben?Welches Sprach- / Modulsystem wird verwendet: Wie schmerzhaft ist ein neues Modul bei Programmierschmerzen und Leistungskosten? Wenn Sie beispielsweise in C / C ++ schreiben, während das Modulsystem COM ist, was den Quellcode schmerzt, alternative Tools erfordert, Auswirkungen auf das Debugging hat und Auswirkungen auf die Leistung hat (für Aufrufe zwischen Modulen), könnte ich dies tun mach eine ernsthafte Pause.
Wenn es sich hingegen um Java- oder C # -DLLs handelt, die sich nahtlos in einer einzigen Ausführungsumgebung kombinieren lassen, ist dies eine andere Sache.
Eine Funktion ist eine Abstraktion und unterstützt DRY.
Gute Abstraktionen müssen jedoch vollständig sein - unvollständige Abstraktionen können sehr wohl dazu führen, dass der konsumierende Client (Programmierer) das Defizit anhand der Kenntnis der zugrunde liegenden Implementierung ausgleicht. Dies führt zu einer engeren Kopplung, als wenn die Abstraktion stattdessen als vollständiger angeboten würde.
Ich würde also versuchen, eine bessere Abstraktion zu schaffen
A
und sich daraufB
zu verlassen, als nur eine einzelne Funktion in ein neues Modul zu verschiebenC
.Ich würde nach einer Reihe von Funktionen suchen, die sich in eine neue Abstraktion einfügen lassen. Das heißt, ich könnte warten, bis die Codebasis weiter fortgeschritten ist, um eine umfassendere / vollständigere Abstraktion zu identifizieren, die umgestaltet werden soll, anstatt auf einer Basis auf einem einzigen Funktionscode erzählen.
quelle
Does A already depend on B or vice versa? — in which case we might have an obvious choice of home for F.
Dies setzt voraus, dass A immer auf B angewiesen ist (oder umgekehrt), was eine sehr gefährliche Annahme ist. Die Tatsache, dass OP F als nicht inhärent Teil von A (oder B) ansieht, legt nahe, dass F existiert, ohne einer der Bibliotheken inhärent zu sein. Wenn F zu einer Bibliothek gehört (z. B. eine DbContext-Erweiterungsmethode (F) und eine Entity Framework-Wrapper-Bibliothek (A oder B)), ist die Frage von OP unbeantwortet.Die Antworten hier, die sich auf alle Möglichkeiten konzentrieren, wie Sie dieses Problem „minimieren“ können, sind für Sie ein schlechter Dienst. Und „Lösungen“, die lediglich verschiedene Möglichkeiten zum Erstellen von Kopplungen bieten, sind überhaupt keine wirklichen Lösungen.
Die Wahrheit ist, dass Sie das von Ihnen geschaffene Problem nicht verstehen. Das Problem mit Ihrem Beispiel hat nichts mit DRY zu tun, sondern (allgemeiner) mit dem Anwendungsdesign.
Fragen Sie sich, warum die Module A und B getrennt sind, wenn beide von derselben Funktion F abhängen? Natürlich werden Sie Probleme mit Abhängigkeitsmanagement / Abstraktion / Kopplung / Sie-Name-es haben, wenn Sie sich zu einem schlechten Design bekennen.
Die ordnungsgemäße Anwendungsmodellierung erfolgt je nach Verhalten. Daher müssen die von F abhängigen Teile von A und B in ein eigenes, unabhängiges Modul extrahiert werden. Ist dies nicht möglich, müssen A und B kombiniert werden. In beiden Fällen sind A und B für das System nicht mehr nützlich und sollten nicht mehr existieren.
DRY ist ein Prinzip, das verwendet werden kann, um schlechtes Design aufzudecken, und nicht, um es zu verursachen. Wenn Sie nicht erreichen können, TROCKEN ( wenn aufgrund der Struktur Ihrer Anwendung dies wirklich zutrifft - und Ihre Bearbeitung notieren), ist dies ein klares Zeichen dafür, dass die Struktur zur Verbindlichkeit geworden ist. Deshalb ist auch das „Continuous Refactoring“ ein Grundsatz.
Das ABC der anderen Gestaltungsprinzipien (fest, trocken, usw.) da draußen sind alle verwendet, um zu ändern (einschließlich Refactoring) einer Anwendung mehr schmerzlos. Konzentrieren Sie sich darauf , und alle anderen Probleme verschwinden allmählich.
quelle
Zumindest für die dritte Option bin ich anderer Meinung:
Aus Ihrer Beschreibung:
Das Einfügen von F in ein C-Modul erhöht die Kopplung nicht, da sowohl A als auch B bereits die C-Funktion benötigen.
quelle