Der übliche Instinkt besteht darin, alle Code-Duplikationen zu entfernen, die Sie im Code sehen. Ich befand mich jedoch in einer Situation, in der die Vervielfältigung illusorisch ist .
Um die Situation genauer zu beschreiben: Ich entwickle eine Webanwendung und die meisten Ansichten sind im Grunde genommen gleich - sie zeigen eine Liste von Elementen an, aus denen der Benutzer scrollen und auswählen kann, eine zweite Liste, die ausgewählte Elemente enthält, und ein "Speichern" ", um die neue Liste zu speichern.
Es schien mir, dass das Problem einfach ist. Allerdings hat jede Ansicht ihre eigenen Macken - manchmal müssen Sie etwas neu berechnen, manchmal müssen Sie einige zusätzliche Daten speichern usw. Diese habe ich durch Einfügen von Callback-Hooks in den Hauptlogikcode gelöst.
Es gibt so viele winzige Unterschiede zwischen den Ansichten, dass sie immer weniger gewartet werden können, da ich Rückrufe für praktisch alle Funktionen bereitstellen muss und die Hauptlogik wie eine riesige Folge von Rückrufaufrufen aussieht. Am Ende spare ich keine Zeit oder Code, weil jede Ansicht einen eigenen Code hat, der ausgeführt wird - alles in Rückrufen.
Die Probleme sind:
- Die Unterschiede sind so gering, dass der Code in allen Ansichten fast genau gleich aussieht.
- Es gibt so viele Unterschiede, dass der Code beim Betrachten der Details kein bisschen gleich ist
Wie soll ich mit dieser Situation umgehen?
Ist es eine gute Lösung, wenn die Kernlogik ausschließlich aus Rückrufaufrufen besteht?
Oder sollte ich lieber den Code duplizieren und die Komplexität von Callback-basiertem Code aufheben?
quelle
Antworten:
Letztendlich müssen Sie ein Urteil darüber fällen, ob Sie ähnlichen Code kombinieren sollen, um Doppelungen zu vermeiden.
Es scheint eine unglückliche Tendenz zu geben, Prinzipien wie "Wiederhole dich nicht" als Regeln zu betrachten, die jederzeit von Hand befolgt werden müssen. Tatsächlich handelt es sich hierbei nicht um universelle Regeln, sondern um Richtlinien, die Ihnen helfen sollen, über gutes Design nachzudenken und es zu entwickeln.
Wie alles im Leben müssen Sie die Vorteile im Vergleich zu den Kosten berücksichtigen. Wie viel duplizierter Code wird entfernt? Wie oft wird der Code wiederholt? Wie viel Aufwand wird es sein, ein allgemeineres Design zu schreiben? Inwieweit werden Sie den Code in Zukunft wahrscheinlich entwickeln? Und so weiter.
Ohne Ihren spezifischen Code zu kennen, ist dies unklar. Vielleicht gibt es eine elegantere Methode, um Duplikate zu entfernen (wie von LindaJeanne vorgeschlagen). Oder vielleicht gibt es einfach nicht genug wahre Wiederholung, um eine Abstraktion zu rechtfertigen.
Unzureichende Aufmerksamkeit für das Design ist eine Falle, aber auch Vorsicht vor Überdesign.
quelle
Denken Sie daran, dass es bei DRY um Wissen geht . Es spielt keine Rolle, ob zwei Codeteile ähnlich, identisch oder völlig unterschiedlich aussehen. Entscheidend ist, ob in beiden das gleiche Wissen über Ihr System vorhanden ist.
Ein Teil des Wissens kann eine Tatsache sein ("die maximal zulässige Abweichung vom beabsichtigten Wert beträgt 0,1%") oder ein Aspekt Ihres Prozesses ("diese Warteschlange enthält niemals mehr als drei Elemente"). Es ist im Wesentlichen jede einzelne Information, die in Ihrem Quellcode kodiert ist.
Wenn Sie also entscheiden, ob es sich bei etwas um eine Vervielfältigung handelt, die entfernt werden sollte, fragen Sie, ob es sich um eine Vervielfältigung von Wissen handelt. Wenn nicht, ist es wahrscheinlich eine zufällige Vervielfältigung. Wenn Sie später eine ähnliche Komponente erstellen möchten, bei der sich das scheinbar vervielfältigte Teil unterscheidet, kann es zu Problemen kommen, wenn Sie es an eine übliche Stelle extrahieren.
quelle
Haben Sie darüber nachgedacht, ein Strategiemuster zu verwenden ? Sie hätten eine View-Klasse, die den gemeinsamen Code und die Routinen enthält, die von mehreren Views aufgerufen werden. Untergeordnete Elemente der View-Klasse enthalten den für diese Instanzen spezifischen Code. Sie alle würden die gemeinsame Schnittstelle verwenden, die Sie für die Ansicht erstellt haben, und daher wären die Unterschiede gekapselt und kohärent.
quelle
Was ist das Potenzial für Veränderungen? Beispielsweise verfügt unsere Anwendung über 8 verschiedene Geschäftsbereiche mit einem Potenzial von 4 oder mehr Benutzertypen für jeden Bereich. Ansichten werden basierend auf dem Benutzertyp und dem Bereich angepasst.
Zu Beginn wurde dieselbe Ansicht verwendet, wobei hier und da einige Überprüfungen durchgeführt wurden, um festzustellen, ob verschiedene Dinge angezeigt werden sollten. Im Laufe der Zeit haben einige Geschäftsbereiche entschieden, drastisch andere Dinge zu tun. Am Ende haben wir im Wesentlichen auf eine Ansicht (Teilansichten im Fall von ASP.NET MVC) pro Funktionsbereich pro Geschäftsbereich migriert. Nicht alle Geschäftsbereiche haben die gleiche Funktionalität, aber wenn einer die Funktionalität eines anderen haben möchte, erhält dieser Bereich eine eigene Ansicht. Es ist viel weniger umständlich für das Verständnis des Codes sowie für die Testbarkeit. Wenn Sie beispielsweise eine Änderung für einen Bereich vornehmen, führt dies nicht zu einer unerwünschten Änderung für einen anderen Bereich.
Wie @ dan1111 bereits erwähnt, kann es zu einem Urteil kommen. Mit der Zeit können Sie feststellen, ob es funktioniert oder nicht.
quelle
Ein Problem könnte sein, dass Sie eine Schnittstelle (theoretische Schnittstelle, keine Sprachfunktion) für nur eine Ebene der Funktionalität bereitstellen:
Anstelle mehrerer Ebenen, je nachdem, wie viel Kontrolle erforderlich ist:
Soweit ich verstanden habe, legen Sie nur die High-Level-Schnittstelle (A) offen und verbergen die Implementierungsdetails (die anderen Dinge dort).
Das Verbergen von Implementierungsdetails hat Vorteile und Sie haben gerade einen Nachteil festgestellt - die Kontrolle ist begrenzt, es sei denn, Sie fügen explizit Funktionen für jede einzelne Sache hinzu, die bei direkter Verwendung der Benutzeroberfläche (n) auf niedriger Ebene möglich gewesen wäre.
Sie haben also zwei Möglichkeiten. Entweder verwenden Sie nur die Low-Level-Schnittstelle, verwenden die Low-Level-Schnittstelle, weil die Pflege der High-Level-Schnittstelle zu aufwendig war, oder Sie legen sowohl High- als auch Low-Level-Schnittstellen offen. Die einzig sinnvolle Option besteht darin, sowohl High- als auch Low-Level-Schnittstellen (und alles dazwischen) anzubieten, vorausgesetzt, Sie möchten redundanten Code vermeiden.
Wenn Sie dann eine andere Ihrer Sachen schreiben, sehen Sie sich alle verfügbaren Funktionen an, die Sie bisher geschrieben haben (unzählige Möglichkeiten, zu entscheiden, welche wiederverwendet werden können), und setzen Sie sie zusammen.
Verwenden Sie ein einzelnes Objekt, bei dem Sie nur wenig Kontrolle benötigen.
Verwenden Sie die Funktionalität der untersten Ebene, wenn eine gewisse Verrücktheit auftreten muss.
Es ist auch nicht sehr schwarz-weiß. Vielleicht kann Ihre große High-Level-Klasse vernünftigerweise alle möglichen Anwendungsfälle abdecken. Vielleicht sind die Anwendungsfälle so unterschiedlich, dass nur die primitive Funktionalität der untersten Ebene ausreicht. Es liegt an Ihnen, das Gleichgewicht zu finden.
quelle
Es gibt bereits andere nützliche Antworten. Ich werde meine hinzufügen.
Vervielfältigung ist schlecht, weil
Der Punkt ist also: Du eliminierst keine Duplizierung, oder weil jemand sagte, es sei wichtig. Sie tun es, weil Sie Fehler / Probleme reduzieren möchten. In Ihrem Fall scheint es, dass Sie, wenn Sie etwas in einer Ansicht ändern, wahrscheinlich nicht dieselbe Zeile in allen anderen Ansichten ändern müssen . Sie haben also scheinbare Vervielfältigung , keine tatsächliche Vervielfältigung.
Ein weiterer wichtiger Punkt ist, niemals etwas von Grund auf neu zu schreiben, was jetzt nur auf der Grundlage eines Prinzips funktioniert, wie Joel sagte (man hätte schon von ihm hören können ...). Wenn Ihre Ansichten also funktionieren, verbessern Sie sich Schritt für Schritt und geraten Sie nicht an den "schlimmsten strategischen Fehler, den ein Software-Unternehmen machen kann".
quelle