Ich arbeite auf einer ziemlich großen Codebasis. Hunderte von Klassen, Tonnen von verschiedenen Dateien, viele Funktionen, es dauert mehr als 15 Minuten, um eine neue Kopie abzurufen usw.
Ein großes Problem bei einer so großen Codebasis ist, dass es einige Dienstprogrammmethoden gibt, die dasselbe tun, oder Code, der diese Dienstprogrammmethoden nicht verwendet, wenn dies möglich ist. Und auch die Utility-Methoden sind nicht alle in einer Klasse (weil es ein riesiges Durcheinander sein würde).
Ich bin ziemlich neu in der Codebasis, aber der Teamleiter, der seit Jahren daran arbeitet, scheint dasselbe Problem zu haben. Es führt zu vielen Code- und Arbeitsduplikationen. Wenn also etwas kaputt geht, werden normalerweise vier Kopien des gleichen Codes erstellt
Wie können wir dieses Muster eindämmen? Wie bei den meisten großen Projekten ist nicht der gesamte Code dokumentiert (obwohl es einige gibt) und nicht der gesamte Code ist ... gut, sauber. Aber im Grunde wäre es wirklich schön, wenn wir daran arbeiten könnten, die Qualität in dieser Hinsicht zu verbessern, damit wir in Zukunft weniger Code-Duplikate haben und Dinge wie Dienstprogramme leichter zu entdecken sind.
Außerdem befinden sich die Dienstprogrammfunktionen normalerweise entweder in einer statischen Hilfsklasse, in einer nicht statischen Hilfsklasse, die für ein einzelnes Objekt arbeitet, oder in einer statischen Methode für die Klasse, bei der sie hauptsächlich "hilft".
Ich hatte ein Experiment zum Hinzufügen von Dienstprogrammfunktionen als Erweiterungsmethoden (ich brauchte keine Interna der Klasse und es war definitiv nur in sehr spezifischen Szenarien erforderlich). Dies hatte zur Folge, dass die Primärklasse und dergleichen nicht überladen wurde, aber es ist nicht mehr wirklich erkennbar, es sei denn, Sie wissen bereits davon
Antworten:
Die einfache Antwort ist, dass Sie eine Codeduplizierung nicht wirklich verhindern können. Sie können das Problem jedoch durch einen schwierigen, sich ständig wiederholenden inkrementellen Prozess beheben, der sich in zwei Schritten zusammensetzt:
Schritt 1. Beginnen Sie mit dem Schreiben von Tests für Legacy-Code (vorzugsweise unter Verwendung eines Testframeworks).
Schritt 2. Schreiben Sie den duplizierten Code neu bzw. überarbeiten Sie ihn mit dem, was Sie aus den Tests gelernt haben
Sie können statische Analysetools verwenden , um doppelten Code zu erkennen, und für C # gibt es eine Vielzahl von Tools, die dies für Sie tun können:
Mit Tools wie diesem können Sie Punkte in Code finden, die ähnliche Aufgaben ausführen. Schreiben Sie weiterhin Tests, um festzustellen, ob dies tatsächlich der Fall ist. Verwenden Sie dieselben Tests, um die Verwendung des doppelten Codes zu vereinfachen. Dieses "Refactoring" kann auf verschiedene Arten durchgeführt werden und Sie können diese Liste verwenden, um die richtige zu bestimmen:
Darüber hinaus gibt es auch ein ganzes Buch von Michael C. Feathers über das effektive Arbeiten mit Legacy-Code . Es geht in die Tiefe verschiedene Strategien, die Sie ergreifen können, um den Code zum Besseren zu ändern. Er hat einen "Legacy-Code-Änderungsalgorithmus", der nicht weit vom obigen zweistufigen Prozess entfernt ist:
Das Buch ist eine gute Lektüre, wenn Sie sich mit Brown-Field-Entwicklung beschäftigen, dh mit Legacy-Code, der geändert werden muss.
In diesem Fall
Im Falle des OP kann ich mir vorstellen, dass der nicht testbare Code durch einen Honigtopf für "Nutzmethoden und Tricks" verursacht wird, die verschiedene Formen annehmen:
Beachten Sie, dass daran nichts auszusetzen ist, sie jedoch normalerweise schwer zu warten und zu ändern sind. Erweiterungsmethoden in .NET sind statische Methoden, aber auch relativ einfach zu testen.
Bevor Sie jedoch mit den Refactorings fertig werden, sprechen Sie mit Ihrem Team darüber. Sie müssen auf der gleichen Seite wie Sie gespeichert sein, bevor Sie fortfahren. Dies liegt daran, dass bei der Umgestaltung von Objekten die Wahrscheinlichkeit hoch ist, dass Zusammenführungskonflikte auftreten. Bevor Sie etwas überarbeiten, untersuchen Sie es, und fordern Sie Ihr Team auf, eine Weile vorsichtig an diesen Codepunkten zu arbeiten, bis Sie fertig sind.
Da das OP neu im Code ist, müssen Sie noch einige andere Dinge tun, bevor Sie etwas tun können:
Viel Glück!
quelle
Wir könnten auch versuchen, das Problem aus einem anderen Blickwinkel zu betrachten. Anstatt zu glauben, das Problem liege in der Vervielfältigung von Code, können wir prüfen, ob das Problem auf das Fehlen von Richtlinien für die Wiederverwendung von Code zurückzuführen ist.
Ich habe kürzlich das Buch Software-Engineering mit wiederverwendbaren Komponenten gelesen. Es enthält in der Tat eine Reihe sehr interessanter Ideen zur Förderung der Wiederverwendbarkeit von Code auf Organisationsebene.
Der Autor dieses Buches, Johannes Sametinger, beschreibt eine Reihe von Hindernissen für die Wiederverwendung von Code, einige konzeptionelle und andere technische. Zum Beispiel:
Je nach Reifegrad einer Organisation ergeben sich nach Angaben des Autors unterschiedliche Wiederverwendbarkeitsebenen.
Vielleicht können Sie neben all den Vorschlägen in anderen Antworten auch ein Wiederverwendbarkeitsprogramm entwerfen, das Management einbeziehen, eine Komponentengruppe bilden, die für die Identifizierung wiederverwendbarer Komponenten verantwortlich ist, indem Sie eine Domänenanalyse durchführen und ein Repository mit wiederverwendbaren Komponenten definieren, das andere Entwickler problemlos verwenden können Fragen und suchen Sie nach gekochten Lösungen für ihre Probleme.
quelle
Es gibt 2 mögliche Lösungen:
Prävention - Versuchen Sie, so gut wie möglich zu dokumentieren. Stellen Sie sicher, dass alle Funktionen ordnungsgemäß dokumentiert und die gesamte Dokumentation einfach durchsucht werden kann. Stellen Sie außerdem beim Schreiben von Code klar, wohin der Code gehen soll, damit klar ist, wo Sie suchen müssen. Die Beschränkung der Anzahl von "Utility" -Codes ist einer der wichtigsten Punkte. Jedes Mal, wenn ich höre, "Lass uns Utility-Klasse machen", steigen meine Haare und mein Blut gefriert, weil es offensichtlich ein Problem ist. Bitten Sie die Benutzer immer schnell und einfach, die Codebasis zu kennen, wenn eine Funktion bereits vorhanden ist.
Lösung - Wenn die Vorbeugung fehlschlägt, sollten Sie in der Lage sein, das problematische Stück Code schnell und effizient zu lösen. Ihr Entwicklungsprozess sollte es ermöglichen, doppelten Code schnell zu reparieren. Unit-Tests sind hierfür perfekt geeignet, da Sie Code effizient ändern können, ohne befürchten zu müssen, dass er beschädigt wird. Wenn Sie also zwei ähnliche Codeteile finden, sollte es mit ein wenig Umgestaltung einfach sein, sie in eine Funktion oder Klasse zu abstrahieren.
Ich persönlich glaube nicht, dass Prävention möglich ist. Je mehr Sie versuchen, desto schwieriger ist es, bereits vorhandene Funktionen zu finden.
quelle
Ich denke nicht, dass diese Art von Problemen die allgemeine Lösung haben. Es wird kein doppelter Code erstellt, wenn die Entwickler ausreichend bereit sind, vorhandenen Code nachzuschlagen. Auch Entwickler konnten die Probleme sofort beheben, wenn sie wollten.
Wenn die Sprache C / C ++ ist, ist das Zusammenführen von Duplikaten aufgrund der Flexibilität der Verknüpfung einfacher (man kann beliebige
extern
Funktionen ohne vorherige Informationen aufrufen ). Für Java oder .NET müssen Sie möglicherweise Hilfsklassen und / oder Dienstprogrammkomponenten entwickeln.Normalerweise beginne ich mit dem Entfernen des vorhandenen Codes durch Duplizieren nur, wenn die Hauptfehler durch die duplizierten Teile verursacht werden.
quelle
Dies ist ein typisches Problem eines größeren Projekts, das von vielen Programmierern bearbeitet wurde, die mitunter unter großem Gruppendruck einen Beitrag geleistet haben. Es ist sehr verlockend, eine Kopie einer Klasse anzufertigen und an diese spezielle Klasse anzupassen. Wenn jedoch ein Problem in der Ursprungsklasse gefunden wurde, sollte es auch in ihren Nachkommen gelöst werden, was oft vergessen wird.
Hierfür gibt es eine Lösung. Sie heißt Generics und wurde in Java 6 eingeführt. Sie entspricht C ++ und heißt Template. Code, dessen genaue Klasse in einer generischen Klasse noch nicht bekannt ist. Bitte suchen Sie nach Java Generics und Sie werden Tonnen und Tonnen an Dokumentation dafür finden.
Ein guter Ansatz ist, Code neu zu schreiben, der an vielen Stellen kopiert / eingefügt zu werden scheint, indem Sie den ersten Code neu schreiben, den Sie zB aufgrund eines bestimmten Fehlers beheben müssen. Schreiben Sie es um, um Generics zu verwenden, und schreiben Sie außerdem sehr strengen Testcode.
Stellen Sie sicher, dass jede Methode der Generic-Klasse aufgerufen wird. Sie können auch Tools zur Codeabdeckung einführen: Generischer Code sollte vollständig codeabgedeckt sein, da er an mehreren Stellen verwendet wird.
Schreiben Sie auch Testcode, z. B. mit JUnit oder ähnlichem für die erste angegebene Klasse, die in Verbindung mit dem generischen Codeteil verwendet werden soll.
Beginnen Sie mit der Verwendung des generischen Codes für die zweite (meistens) kopierte Version, wenn der gesamte vorhergehende Code funktioniert und vollständig getestet wurde. Sie werden sehen, dass es einige Codezeilen gibt, die für diese bestimmte Klasse spezifisch sind. Sie können diese Codezeilen in einer abstrakten geschützten Methode aufrufen, die von der abgeleiteten Klasse implementiert werden muss, die die Basisklasse Generic verwendet.
Ja, es ist eine mühsame Aufgabe, aber mit der Zeit wird es immer besser, ähnliche Klassen herauszureißen und durch etwas zu ersetzen, das sehr, sehr sauber, gut geschrieben und viel einfacher zu warten ist.
Ich hatte eine ähnliche Situation, in der in einer allgemeinen Klasse irgendwann 6 oder 7 andere fast identische Klassen ersetzt wurden, die alle fast identisch waren, aber über einen bestimmten Zeitraum von verschiedenen Programmierern kopiert und eingefügt wurden.
Und ja, ich bin sehr für ein automatisiertes Testen des Codes. Am Anfang wird es mehr kosten, aber es wird Ihnen auf jeden Fall enorm viel Zeit sparen. Versuchen Sie außerdem, eine Codeabdeckung von insgesamt mindestens 80% und 100% für generischen Code zu erreichen.
Hoffe das wird helfen und viel Glück.
quelle
Ich werde die am wenigsten verbreitete Meinung hier wiederholen
Gangnus
und behaupten, dass Code-Vervielfältigung nicht immer schädlich ist und manchmal das geringere Übel sein könnte.Wenn Sie mir zum Beispiel die Möglichkeit geben, Folgendes zu verwenden:
A) Eine stabile (unveränderliche) und winzige, gut getestete Bildbibliothek, die ein paar Dutzend Zeilen trivialen mathematischen Codes für Vektormathematik wie Punktprodukte und Lerps und Klemmen dupliziert, sich jedoch vollständig von allem anderen entkoppelt und in einem Bruchteil von erstellt eine Sekunde.
B) Eine instabile (sich schnell ändernde) Bildbibliothek, die von einer epischen Mathematikbibliothek abhängt, um das oben erwähnte Dutzend Codezeilen zu vermeiden, wobei die Mathematikbibliothek instabil ist und ständig neue Aktualisierungen und Änderungen erhält und daher auch die Bildbibliothek muss wieder aufgebaut werden, wenn nicht auch komplett geändert. Es dauert 15 Minuten, um das Ganze sauber aufzubauen.
... dann sollte es für die meisten Menschen ein Kinderspiel sein, dass A, und zwar gerade wegen seiner geringfügigen Code-Duplizierung, vorzuziehen ist. Der Schwerpunkt, den ich setzen muss, ist der erprobte Teil. Offensichtlich gibt es nichts Schlimmeres, als duplizierten Code zu haben, der überhaupt nicht funktioniert. An diesem Punkt werden Fehler dupliziert.
Aber es gibt auch Kopplung und Stabilität zu bedenken, und einige bescheidene Duplizierungen können hier und da als Entkopplungsmechanismus dienen, der auch die Stabilität (unveränderliche Natur) des Pakets erhöht.
Mein Vorschlag wird also sein, mich mehr auf das Testen und Erarbeiten von etwas wirklich Stabilem zu konzentrieren (wie etwa Unveränderlichem, Finden weniger Gründe, um sich in der Zukunft zu ändern) und Zuverlässigem, dessen Abhängigkeiten von externen Quellen, falls vorhanden, bestehen Sehr stabil, da versucht wurde, alle Formen der Vervielfältigung in Ihrer Codebasis auszumerzen. In einer Umgebung mit großen Teams ist letzteres in der Regel ein unpraktisches Ziel, ganz zu schweigen davon, dass es die Kopplung und die Menge an instabilem Code in Ihrer Codebasis erhöhen kann.
quelle
Vergessen Sie nicht, dass die Code-Vervielfältigung nicht immer schädlich ist. Stellen Sie sich vor: Jetzt müssen Sie einige Aufgaben in absolut unterschiedlichen Modulen Ihres Projekts lösen. Gerade jetzt ist es die gleiche Aufgabe.
Dafür kann es drei Gründe geben:
Einige Themen rund um diese Aufgabe sind für beide Module gleich. In diesem Fall ist die Code-Vervielfältigung schlecht und sollte liquidiert werden. Es wäre klug, eine Klasse oder ein Modul zur Unterstützung dieses Themas zu erstellen und seine Methoden in beiden Modulen zu verwenden.
Die Aufgabe ist in Bezug auf Ihr Projekt theoretisch. Zum Beispiel ist es aus der Physik oder Mathematik usw. Die Aufgabe existiert unabhängig von Ihrem Projekt. In diesem Fall ist die Code-Vervielfältigung schlecht und sollte auch liquidiert werden. Ich würde eine spezielle Klasse für solche Funktionen erstellen. Verwenden Sie eine solche Funktion in jedem Modul, in dem Sie sie benötigen.
In anderen Fällen ist das Zusammentreffen von Aufgaben ein vorübergehendes Zusammentreffen und nichts anderes. Es wäre gefährlich zu glauben, dass diese Aufgaben während Änderungen des Projekts aufgrund von Refactoring und sogar Debugging gleich bleiben. In diesem Fall ist es besser , zwei gleiche Funktionen / Codeteile an verschiedenen Stellen zu erstellen. Und zukünftige Änderungen in einem von ihnen werden den anderen nicht berühren.
Und dieser dritte Fall kommt sehr oft vor. Wenn Sie "unwissentlich" duplizieren, geschieht dies meistens aus diesem Grund - es handelt sich nicht um eine echte Duplizierung!
Versuchen Sie also, es sauber zu halten, wenn es wirklich notwendig ist, und fürchten Sie sich nicht vor Vervielfältigungen, wenn es nicht das Muss ist.
quelle
code duplication is not always harmful
ist ein schlechter Rat.