Codeduplizierung ohne offensichtliche Abstraktion

14

Haben Sie jemals einen Fall von Code-Duplizierung erlebt, bei dem Sie beim Betrachten der Codezeilen keine thematische Abstraktion finden konnten, die die Rolle des Codes in der Logik genau beschreibt? Und was hast du getan, um das anzugehen?

Es handelt sich um eine Code-Duplizierung, daher müssen wir im Idealfall eine Refrakturierung durchführen, um sie beispielsweise zu ihrer eigenen Funktion zu machen. Aber da der Code keine gute Abstraktion hat, um ihn zu beschreiben, wäre das Ergebnis eine seltsame Funktion, für die wir nicht einmal einen guten Namen finden können und deren Rolle in der Logik nicht nur beim Betrachten offensichtlich ist. Das schmerzt für mich die Klarheit des Codes. Wir können Klarheit bewahren und es so lassen, wie es ist, aber dann verletzen wir die Wartbarkeit.

Was ist Ihrer Meinung nach der beste Weg, um so etwas anzusprechen?

EpsilonVector
quelle

Antworten:

18

Manchmal ist Code-Duplikation das Ergebnis eines "Wortspiels": Zwei Dinge sehen gleich aus, sind es aber nicht.

Möglicherweise kann eine Überabstraktion die wahre Modularität Ihres Systems sprengen. Im Rahmen der Modularität müssen Sie sich entscheiden, "was sich voraussichtlich ändern wird". und "was ist stabil?". Was stabil ist, wird in die Schnittstelle gestellt, während was instabil ist, in die Implementierung des Moduls eingeschlossen wird. Wenn sich die Dinge ändern, wird die Änderung, die Sie vornehmen müssen, auf dieses Modul beschränkt.

Refactoring ist erforderlich, wenn das, was Sie für stabil hielten (z. B. dieser API-Aufruf benötigt immer zwei Argumente), geändert werden muss.

Für diese beiden duplizierten Codefragmente würde ich also fragen: Bedeutet eine Änderung an dem einen unbedingt, dass auch der andere geändert werden muss?

Wie Sie diese Frage beantworten, gibt Ihnen möglicherweise einen besseren Einblick in das, was eine gute Abstraktion sein könnte.

Entwurfsmuster sind auch nützliche Werkzeuge. Möglicherweise durchläuft Ihr duplizierter Code eine bestimmte Form, und das Iterationsmuster sollte angewendet werden.

Wenn Ihr duplizierter Code mehrere Rückgabewerte enthält (und Sie deshalb keine einfache Extraktionsmethode ausführen können), sollten Sie möglicherweise eine Klasse erstellen, die die zurückgegebenen Werte enthält. Die Klasse kann für jeden Punkt eine abstrakte Methode aufrufen, die zwischen den beiden Codefragmenten variiert. Sie würden dann zwei konkrete Implementierungen der Klasse vornehmen: eine für jedes Fragment. [Dies ist effektiv das Entwurfsmuster der Vorlagenmethode, nicht zu verwechseln mit dem Konzept der Vorlagen in C ++. Alternativ könnte das, was Sie sich ansehen, mit dem Strategiemuster besser gelöst werden.]

Eine andere natürliche und nützliche Art, darüber nachzudenken, sind Funktionen höherer Ordnung. Zum Beispiel Lambdas erstellen oder anonyme innere Klassen verwenden, damit der Code an die Abstraktion übergeben wird. Im Allgemeinen können Sie Duplikate entfernen, aber wenn es nicht wirklich eine Beziehung zwischen ihnen gibt (wenn sich eines ändert, muss sich das andere ändern), kann dies die Modularität beeinträchtigen, ohne Abhilfe zu schaffen.

Macneil
quelle
4

Wenn Sie auf eine solche Situation stoßen, denken Sie am besten an "nicht-traditionelle" Abstraktionen. Vielleicht haben Sie eine Menge Duplikate innerhalb einer Funktion, und das Faktorisieren einer einfachen alten Funktion passt nicht sehr gut, weil Sie zu viele Variablen übergeben müssen. Hier würde eine geschachtelte D / Python-Funktion (mit Zugriff auf den äußeren Bereich) großartig funktionieren. (Ja, Sie könnten eine Klasse erstellen, die diesen gesamten Status beibehält. Wenn Sie sie jedoch nur in zwei Funktionen verwenden, ist dies eine hässliche und ausführliche Problemumgehung, da keine verschachtelten Funktionen vorhanden sind.) Vielleicht passt die Vererbung nicht ganz, aber a mixin würde gut funktionieren. Vielleicht brauchen Sie wirklich ein Makro. Vielleicht sollten Sie eine Vorlage Metaprogrammierung oder Reflexion / Introspektion oder sogar generative Programmierung in Betracht ziehen.

Aus pragmatischer Sicht sind dies natürlich alle schwierig oder gar unmöglich, wenn Ihre Sprache sie nicht unterstützt und nicht über genügend Metaprogrammierungsfunktionen verfügt, um sie sauber in der Sprache zu implementieren. Wenn dies der Fall ist, weiß ich nicht, was ich Ihnen sagen soll, außer "eine bessere Sprache bekommen". Das Erlernen einer Hochsprache mit vielen Abstraktionsfähigkeiten (wie Ruby, Python, Lisp oder D) kann Ihnen außerdem dabei helfen, besser in Niedrigsprachen zu programmieren, in denen einige der Techniken möglicherweise noch verwendbar, aber weniger offensichtlich sind.

dsimcha
quelle
+1 für viele exzellente Techniken auf engstem Raum. (Nun, wäre +1 für die beschriebenen Techniken auch gewesen.)
Macneil
3

Persönlich ignoriere ich es und gehe weiter. Wenn es so seltsam ist, dass es besser ist, es duplizieren zu lassen, könnten Sie Ewigkeiten damit verbringen, es zu überarbeiten, und der nächste Entwickler wird einen Blick darauf werfen und Ihre Änderung rückgängig machen!

Toby
quelle
2

Ohne ein Codebeispiel ist es schwierig zu sagen, warum Ihr Code keine leicht identifizierbare Abstraktion aufweist. Mit dieser Einschränkung, hier sind ein paar Ideen:

  • Anstatt eine neue Funktion für den gemeinsamen Code zu erstellen, teilen Sie die Funktionalität in verschiedene Teile auf.
  • kleine Stücke basierend auf gängigen Datentypen oder abstraktem Verhalten zu Gruppen zusammenfassen;
  • schreiben Sie den doppelten Code angesichts der neuen Stücke um;
  • Wenn sich der neue Code immer noch einer klaren Abstraktion entzieht, zerlegen Sie ihn ebenfalls in kleine Teile und wiederholen Sie den Vorgang.

Die größte Schwierigkeit in dieser Übung besteht darin, dass Ihre Funktion wahrscheinlich zu viele unabhängige Verhaltensweisen auf einer bestimmten Abstraktionsebene enthält und Sie einige davon auf einer niedrigeren Ebene behandeln müssen. Sie vermuten zu Recht, dass Klarheit der Schlüssel zur Aufrechterhaltung des Codes ist, aber das Verhalten des Codes (sein aktueller Zustand) deutlich zu machen, unterscheidet sich stark von der Absicht des Codes.

Machen Sie das Wie der kleineren Codeteile abstrakt, indem Sie ihre Funktionssignaturen das Was identifizieren lassen, und die größeren Teile sollten einfacher zu klassifizieren sein.

Huperniketes
quelle