Wie vermeide ich die Kaskadierung von Refactorings?

52

Ich habe ein Projekt. In diesem Projekt wollte ich es umgestalten, um ein Feature hinzuzufügen, und ich habe das Projekt umgestaltet, um das Feature hinzuzufügen.

Das Problem ist, dass es sich herausstellte, als ich fertig war, dass ich eine geringfügige Änderung an der Benutzeroberfläche vornehmen musste, um sie aufzunehmen. Also habe ich die Änderung vorgenommen. Und dann kann die konsumierende Klasse nicht mit ihrer aktuellen Schnittstelle in Bezug auf die neue implementiert werden, weshalb sie auch eine neue Schnittstelle benötigt. Jetzt ist es drei Monate später, und ich musste unzählige Probleme beheben, die praktisch nichts miteinander zu tun haben, und ich bin auf der Suche nach Lösungen für Probleme, die in einem Jahr geplant wurden oder die aufgrund von Schwierigkeiten nicht behoben werden können, bevor die Sache kompiliert werden kann nochmal.

Wie kann ich diese Art von kaskadierenden Refactorings in Zukunft vermeiden? Ist es nur ein Symptom für meine früheren Klassen, die zu eng voneinander abhängen?

Kurze edit: In diesem Fall ist das Refactoring war das Merkmal, da das Umgestalten der Dehnbarkeit eines bestimmten Stückes Code erhöht und eine Kopplung verringert. Dies bedeutete, dass externe Entwickler mehr tun konnten, was die Funktion war, die ich liefern wollte. Der ursprüngliche Refactor selbst sollte also keine funktionale Änderung sein.

Größere Änderung, die ich vor fünf Tagen versprochen habe:

Bevor ich mit diesem Refactor anfing, hatte ich ein System mit einer Schnittstelle, aber bei der Implementierung habe ich einfach dynamic_castalle möglichen Implementierungen durchgearbeitet, die ich ausgeliefert habe. Dies bedeutete offensichtlich, dass Sie zum einen nicht einfach von der Schnittstelle erben konnten und zum anderen, dass es für niemanden ohne Implementierungszugriff möglich wäre, diese Schnittstelle zu implementieren. Deshalb habe ich beschlossen, dieses Problem zu beheben und die Schnittstelle für den öffentlichen Verbrauch zu öffnen, damit jeder sie implementieren kann, und die Implementierung der Schnittstelle war der gesamte erforderliche Vertrag - offensichtlich eine Verbesserung.

Als ich alle Orte gefunden und mit Feuer getötet hatte, an denen ich dies getan hatte, fand ich einen Ort, der sich als besonderes Problem herausstellte. Es hing von den Implementierungsdetails aller verschiedenen Ableitungsklassen und der duplizierten Funktionalität ab, die bereits implementiert waren, aber an einer anderen Stelle besser. Es hätte stattdessen in Form der öffentlichen Schnittstelle implementiert und die vorhandene Implementierung dieser Funktionalität wiederverwendet werden können. Ich entdeckte, dass es eines bestimmten Kontextes bedurfte, um richtig zu funktionieren. Grob gesagt sah die aufrufende vorherige Implementierung irgendwie so aus

for(auto&& a : as) {
     f(a);
}

Um diesen Kontext zu erhalten, musste ich ihn jedoch in etwas Ähnliches ändern

std::vector<Context> contexts;
for(auto&& a : as)
    contexts.push_back(g(a));
do_thing_now_we_have_contexts();
for(auto&& con : contexts)
    f(con);

Dies bedeutet, dass für alle Vorgänge, zu denen früher ein Teil gehörte f, einige von ihnen zu einem Teil der neuen Funktion gemacht werden müssen g, die ohne Kontext ausgeführt wird, und einige von ihnen zu einem Teil der jetzt zurückgestellten f. Aber nicht alle Methoden fbenötigen oder wollen diesen Kontext - einige von ihnen benötigen einen bestimmten Kontext, den sie auf unterschiedliche Weise erhalten. Also musste ich für alles, was fletztendlich aufruft (was grob gesagt so ziemlich alles ist ), bestimmen, welchen Kontext sie brauchten, woher sie ihn nehmen sollten und wie sie von alt fnach neu fund von neu zu trennen waren g.

Und so bin ich dort gelandet, wo ich jetzt bin. Der einzige Grund, warum ich so weitermachte, war, dass ich dieses Refactoring sowieso aus anderen Gründen brauchte.

DeadMG
quelle
67
Wenn Sie sagen, dass Sie das Projekt überarbeitet haben, um ein Feature hinzuzufügen, was meinen Sie dann genau? Refactoring verändert per Definition nicht das Verhalten von Programmen, was diese Aussage verwirrend macht.
Jules,
5
@Jules: Genau genommen bestand die Funktion darin, anderen Entwicklern das Hinzufügen eines bestimmten Erweiterungstyps zu ermöglichen. Die Funktion war also der Refactor, der die Klassenstruktur offener machte.
DeadMG
5
Ich dachte, das wird in jedem Buch und Artikel besprochen, in dem es um Refactoring geht. Quellcodeverwaltung kommt zur Rettung; Wenn Sie feststellen, dass Schritt A ausgeführt werden soll, müssen Sie zuerst Schritt B ausführen, dann zuerst Schritt A und dann Schritt B ausführen.
rwong
4
@DeadMG: Dies ist das Buch, das ich ursprünglich in meinem ersten Kommentar zitieren wollte: "Das Spiel" pick-up sticks "ist eine gute Metapher für die Mikado-Methode. Sie eliminieren" technische Schulden "- die Altprobleme, die in fast jeder Software enthalten sind System - indem Sie eine Reihe von leicht zu implementierenden Regeln befolgen. Sie extrahieren sorgfältig jede verflochtene Abhängigkeit, bis Sie das zentrale Problem aufdecken, ohne das Projekt zu reduzieren. "
rwong
2
Können Sie erklären, über welche Programmiersprache wir sprechen? Nachdem ich alle Ihre Kommentare gelesen habe, komme ich zu dem Schluss, dass Sie dies von Hand tun, anstatt eine IDE zu verwenden, um Sie zu unterstützen. Daher würde ich gerne wissen, ob ich Ihnen einen praktischen Rat geben kann.
thepacker

Antworten:

69

Als ich das letzte Mal versuchte, ein Refactoring mit unvorhergesehenen Konsequenzen zu starten und den Build und / oder die Tests nach einem Tag nicht mehr stabilisieren konnte , gab ich auf und stellte die Codebasis auf den Punkt vor dem Refactoring zurück.

Dann begann ich zu analysieren, was schief gelaufen war, und entwickelte einen besseren Plan, um das Refactoring in kleineren Schritten durchzuführen. Mein Ratschlag zur Vermeidung von kaskadierenden Refactorings lautet daher: Weiß, wann aufzuhören ist , und lass die Dinge nicht außer Kontrolle geraten!

Manchmal muss man in die Kugel beißen und einen ganzen Arbeitstag wegwerfen - auf jeden Fall einfacher als drei Monate Arbeit wegzuwerfen. Der Tag, an dem Sie verlieren, ist nicht ganz umsonst, zumindest haben Sie gelernt , das Problem nicht anzugehen. Und meiner Erfahrung nach gibt es immer Möglichkeiten, kleinere Schritte beim Refactoring zu machen.

Randnotiz : Sie scheinen sich in einer Situation zu befinden, in der Sie entscheiden müssen, ob Sie bereit sind, volle drei Monate Arbeit zu opfern und mit einem neuen (und hoffentlich erfolgreicheren) Refactoring-Plan von vorne zu beginnen. Ich kann mir vorstellen, dass dies keine einfache Entscheidung ist, aber fragen Sie sich, wie hoch das Risiko ist, dass Sie weitere drei Monate benötigen, um nicht nur den Build zu stabilisieren, sondern auch um alle unvorhergesehenen Fehler zu beheben, die Sie wahrscheinlich während Ihres Umschreibens in den letzten drei Monaten verursacht haben ? Ich habe "rewrite" geschrieben, weil ich denke, das ist, was Sie wirklich getan haben, kein "Refactoring". Es ist nicht unwahrscheinlich, dass Sie Ihr aktuelles Problem schneller lösen können, indem Sie zu der letzten Revision zurückkehren, in der Ihr Projekt kompiliert wurde, und mit einem echten Refactoring beginnen (im Gegensatz zu "Rewrite").

Doc Brown
quelle
53

Ist es nur ein Symptom für meine früheren Klassen, die zu eng voneinander abhängen?

Sicher. Eine Änderung, die unzählige andere Änderungen hervorruft, ist so ziemlich die Definition der Kopplung.

Wie vermeide ich die Kaskadierung von Refaktoren?

In der schlimmsten Art von Codebasen wird eine einzelne Änderung weiterhin kaskadiert, was schließlich dazu führt, dass Sie (fast) alles ändern. Ein Teil eines Refaktors, bei dem eine weit verbreitete Kopplung besteht, besteht darin, den Teil zu isolieren, an dem Sie arbeiten. Sie müssen nicht nur umgestalten, wo Ihre neue Funktion diesen Code berührt, sondern wo alles andere diesen Code berührt.

Normalerweise bedeutet dies, dass einige Adapter verwendet werden, damit der alte Code mit etwas funktioniert, das wie der alte Code aussieht und funktioniert, aber die neue Implementierung / Schnittstelle verwendet. Wenn Sie nur die Schnittstelle / Implementierung ändern, aber die Kopplung verlassen, erhalten Sie schließlich nichts. Es ist Lippenstift auf einem Schwein.

Telastyn
quelle
33
+1 Je schlimmer ein Refactoring benötigt wird, desto weiter wird das Refactoring reichen. Es liegt in der Natur der Sache.
Paul Draper
4
Wenn Sie jedoch wirklich umgestalten , sollte sich anderer Code nicht sofort mit den Änderungen befassen müssen. (Natürlich möchten Sie irgendwann die anderen Teile bereinigen ... aber das sollte nicht sofort erforderlich sein.) Eine Änderung, die über den Rest der App "kaskadiert", ist größer als Refactoring - an diesem Punkt ist es Grundsätzlich eine Neugestaltung oder Umschreibung.
CHAO
+1 Mit einem Adapter können Sie genau den Code isolieren, den Sie zuerst ändern möchten.
Winkbrace
17

Es hörte sich so an, als wäre Ihr Refactoring zu ehrgeizig. Ein Refactoring sollte in kleinen Schritten durchgeführt werden, von denen jeder in (etwa) 30 Minuten - oder im schlimmsten Fall höchstens einem Tag - abgeschlossen werden kann. Das Projekt kann dann noch erstellt werden, und alle Tests sind noch nicht abgeschlossen.

Wenn Sie jede einzelne Änderung auf ein Minimum beschränken, sollte es für ein Refactoring nicht möglich sein, Ihren Build für längere Zeit zu brechen. Der schlimmste Fall ist wahrscheinlich das Ändern der Parameter in eine Methode in einer weit verbreiteten Schnittstelle, z. B. um einen neuen Parameter hinzuzufügen. Die daraus resultierenden Änderungen sind jedoch mechanisch: Hinzufügen (und Ignorieren) des Parameters in jeder Implementierung und Hinzufügen eines Standardwerts in jedem Aufruf. Selbst wenn es Hunderte von Referenzen gibt, sollte es nicht einmal einen Tag dauern, um ein solches Refactoring durchzuführen.

Jules
quelle
4
Ich sehe nicht ein, wie eine solche Situation entstehen kann. Für ein angemessenes Refactoring der Schnittstelle einer Methode muss ein leicht zu bestimmender neuer Parametersatz übergeben werden können, der dazu führt, dass das Verhalten des Aufrufs dem vor der Änderung entspricht.
Jules
3
Ich war noch nie in einer Situation, in der ich ein solches Refactoring durchführen wollte, aber ich muss sagen, dass es für mich ziemlich ungewöhnlich klingt. Wollen Sie damit sagen, dass Sie die Funktionalität von der Benutzeroberfläche entfernt haben? Wenn ja, wohin ist es gegangen? In eine andere Schnittstelle? Oder irgendwo anders?
Jules
5
Dann müssen Sie alle Verwendungen des zu entfernenden Features entfernen, bevor Sie das Refactoring durchführen, um es zu entfernen, und nicht danach. Auf diese Weise können Sie den Code weiterentwickeln, während Sie daran arbeiten.
Jules
11
@DeadMG: das klingt seltsam: Sie entfernen ein Feature, das nicht mehr benötigt wird, wie Sie sagen. Auf der anderen Seite schreiben Sie "das Projekt wird völlig funktionsunfähig" - das hört sich tatsächlich so an, als ob die Funktion unbedingt benötigt wird. Bitte klären Sie.
Doc Brown
26
@DeadMG In solchen Fällen würden Sie normalerweise das neue Feature entwickeln, Tests hinzufügen, um sicherzustellen, dass es funktioniert, vorhandenen Code für die Verwendung der neuen Schnittstelle umstellen und dann das (jetzt) ​​überflüssige alte Feature entfernen. Auf diese Weise sollte es keinen Punkt geben, an dem etwas kaputt geht.
Sapi
12

Wie kann ich diese Art von kaskadierendem Refaktor in Zukunft vermeiden?

Wunschdenken Design

Das Ziel ist ein exzellentes OO-Design und die Implementierung des neuen Features. Das Vermeiden von Refactoring ist ebenfalls ein Ziel.

Beginnen Sie bei Null und entwerfen Sie ein Design für die neue Funktion , die Sie sich gewünscht haben. Nehmen Sie sich Zeit, es gut zu machen.

Beachten Sie jedoch, dass der Schlüssel hier "eine Funktion hinzufügen" ist. Neue Sachen neigen dazu, die aktuelle Struktur der Codebasis weitgehend zu ignorieren. Unser Wunschdenken Design ist unabhängig. Dann brauchen wir aber noch zwei Dinge:

  • Refactor reicht nur aus, um eine notwendige Naht zu erstellen, um den Code des neuen Features einzufügen / zu implementieren.
    • Widerstand gegen Refactoring sollte das neue Design nicht antreiben.
  • Schreiben Sie eine clientseitige Klasse mit einer API, die das neue Feature und die vorhandenen Codez so gestaltet, dass sie sich gegenseitig nicht kennen.
    • Es transkribiert, um Objekte, Daten und Ergebnisse hin und her zu erhalten. Prinzip des geringsten Wissens sei verdammt. Wir werden nichts Schlimmeres tun als das, was bereits vorhandener Code tut.

Heuristiken, Lektionen gelernt, etc.

Refactoring war so einfach wie das Hinzufügen eines Standardparameters zu einem vorhandenen Methodenaufruf. oder ein einzelner Aufruf einer statischen Klassenmethode.

Erweiterungsmethoden für vorhandene Klassen können dazu beitragen, die Qualität des neuen Designs bei einem absolut minimalen Risiko zu erhalten.

"Struktur" ist alles. Struktur ist die Verwirklichung des Einzelverantwortungsprinzips; Design, das Funktionalität erleichtert. Code bleibt in der gesamten Klassenhierarchie kurz und einfach. Zeit für neues Design wird beim Testen, Überarbeiten und Vermeiden von Hackerangriffen durch den alten Code-Dschungel aufgewendet.

Wunschdenken Klassen konzentrieren sich auf die jeweilige Aufgabe. Vergessen Sie im Allgemeinen, eine vorhandene Klasse zu erweitern - Sie lösen nur die Refactorkaskade erneut aus und müssen sich mit dem Overhead der "schwereren" Klasse befassen.

Entfernen Sie alle Reste dieser neuen Funktionalität aus dem vorhandenen Code. Hier ist eine vollständige und gut gekapselte Funktionalität neuer Features wichtiger als die Vermeidung von Refactoring.

Radarbob
quelle
9

Aus dem (wunderbaren) Buch Working Effectively with Legacy Code von Michael Feathers :

Wenn Sie Abhängigkeiten in Legacy-Code auflösen, müssen Sie häufig Ihren Sinn für Ästhetik ein wenig aufheben. Einige Abhängigkeiten sind fehlerfrei. andere sehen aus gestalterischer Sicht alles andere als ideal aus. Sie sind wie die Schnittpunkte in der Chirurgie: Nach Ihrer Arbeit kann eine Narbe in Ihrem Code zurückbleiben, aber alles darunter kann besser werden.

Wenn Sie später Code abdecken können, an dem Sie die Abhängigkeiten aufgehoben haben, können Sie auch diese Narbe heilen.

Kenny Evitt
quelle
6

Es hört sich so an, als hätten Sie sich (insbesondere aufgrund der Diskussionen in Kommentaren) selbst mit selbst auferlegten Regeln konfrontiert, was bedeutet, dass diese "geringfügige" Änderung den gleichen Arbeitsaufwand bedeutet wie ein vollständiges Umschreiben der Software.

Die Lösung muss lauten: "Dann tu das nicht" . Das passiert in echten Projekten. Viele alte APIs haben hässliche Schnittstellen oder aufgegebene (immer null) Parameter oder Funktionen mit dem Namen DoThisThing2 (), die dasselbe tun wie DoThisThing () mit einer völlig anderen Parameterliste. Andere häufige Tricks sind das Speichern von Informationen in Globals oder das Markieren von Zeigern, um sie an einem großen Teil des Frameworks vorbeizuschleusen. (Zum Beispiel habe ich ein Projekt, in dem die Hälfte der Audiopuffer nur einen magischen 4-Byte-Wert enthält, da dies viel einfacher war, als zu ändern, wie eine Bibliothek ihre Audiocodecs aufrief.)

Ohne spezifischen Code ist es schwierig, spezifische Ratschläge zu geben.

pjc50
quelle
3

Automatisierte Tests. Sie müssen kein TDD-Fanatiker sein und benötigen keine 100-prozentige Abdeckung. Mit automatisierten Tests können Sie jedoch sicher Änderungen vornehmen. Außerdem klingt es so, als hätten Sie ein Design mit sehr hoher Kopplung. Lesen Sie mehr über die SOLID-Prinzipien, die speziell für diese Art von Problemen im Software-Design formuliert wurden.

Ich würde diese Bücher auch empfehlen.

  • Effektiv mit Legacy-Code arbeiten , Federn
  • Refactoring , Fowler
  • Wachsende objektorientierte Software, angeleitet von Tests , Freeman und Pryce
  • Sauberer Code , Martin
asthasr
quelle
3
Ihre Frage lautet: "Wie vermeide ich dies in Zukunft?" Die Antwort ist, dass Sie CI und Tests nicht korrekt anwenden, auch wenn Sie sie gerade haben. Ich habe keinen Kompilierungsfehler festgestellt, der mehr als zehn Minuten in den letzten Jahren gedauert hat, da ich das Kompilieren als "den ersten Komponententest" betrachte. Wenn der Fehler behoben ist, muss ich in der Lage sein, Tests als bestanden zu erkennen Ich arbeite weiter am Code.
Asthasr
6
Wenn ich eine stark genutzte Benutzeroberfläche überarbeite, füge ich ein Shim hinzu. Dieser Shim übernimmt die Standardeinstellung, sodass Legacy-Aufrufe weiterhin funktionieren. Ich arbeite an der Schnittstelle hinter dem Shim. Wenn ich damit fertig bin, ändere ich die Klassen, um die Schnittstelle anstelle des Shims wieder zu verwenden.
Asthasr
5
Trotz fehlgeschlagener Builds die Umgestaltung fortzusetzen, ist mit einem Totalausfall vergleichbar . Es ist eine Navigationstechnik der letzten Instanz . Beim Refactoring ist es möglich, dass eine Refactoring-Richtung einfach falsch ist, und Sie haben bereits das verräterische Zeichen dafür gesehen (in dem Moment, in dem es nicht mehr kompiliert wird, dh ohne Fluggeschwindigkeitsindikatoren zu fliegen), aber Sie haben beschlossen, weiterzumachen. Schließlich fällt das Flugzeug vom Radar. Glücklicherweise brauchen wir keine Blackbox oder Ermittler für das Refactoring: Wir können immer "den zuletzt bekannten guten Zustand wiederherstellen".
rwong
4
@DeadMG: du hast geschrieben "In meinem Fall machen die vorherigen Aufrufe einfach keinen Sinn mehr", aber in deiner Frage "eine kleine Schnittstellenänderung, um sie aufzunehmen". Ehrlich gesagt kann nur einer dieser beiden Sätze wahr sein. Und aus Ihrer Problembeschreibung geht hervor, dass Ihre Schnittstellenänderung definitiv keine geringfügige Änderung war . Sie sollten wirklich, wirklich gründlicher darüber nachdenken, wie Sie Ihre Änderungen abwärtskompatibler gestalten können. Meiner Erfahrung nach ist das immer möglich, aber man muss sich zuerst einen guten Plan ausdenken.
Doc Brown
3
@DeadMG In diesem Fall kann das, was Sie tun, meiner Meinung nach nicht als Refactoring bezeichnet werden, dessen grundlegender Punkt darin besteht, Designänderungen in einer Reihe sehr einfacher Schritte durchzuführen.
Jules
3

Ist es nur ein Symptom für meine früheren Klassen, die zu eng voneinander abhängen?

Höchstwahrscheinlich ja. Obwohl Sie ähnliche Effekte mit einer ziemlich netten und sauberen Codebasis erzielen können, wenn sich die Anforderungen genug ändern

Wie kann ich diese Art von kaskadierenden Refactorings in Zukunft vermeiden?

Abgesehen davon, dass Sie aufhören, an Legacy-Code zu arbeiten, haben Sie keine Angst. Sie können jedoch eine Methode verwenden, die den Effekt vermeidet, dass für Tage, Wochen oder sogar Monate keine Arbeitscodebasis vorhanden ist.

Diese Methode heißt "Mikado-Methode" und funktioniert folgendermaßen:

  1. Schreiben Sie das gewünschte Ziel auf ein Blatt Papier

  2. Nehmen Sie die einfachste Änderung vor, die Sie in diese Richtung führt.

  3. Überprüfen Sie, ob es mit dem Compiler und Ihrer Testsuite funktioniert. Fahren Sie andernfalls mit Schritt 4 fort.

  4. Notieren Sie auf Ihrem Papier die Dinge, die geändert werden müssen, damit Ihre aktuelle Änderung funktioniert. Zeichne Pfeile von deiner aktuellen Aufgabe zu den neuen.

  5. Machen Sie Ihre Änderungen rückgängig Dies ist der wichtige Schritt. Es ist kontraintuitiv und tut am Anfang körperlich weh, aber da Sie nur eine einfache Sache ausprobiert haben, ist es eigentlich nicht so schlimm.

  6. Wählen Sie eine der Aufgaben aus, die keine ausgehenden Fehler aufweist (keine bekannten Abhängigkeiten), und kehren Sie zu 2 zurück.

  7. Übernehmen Sie die Änderung, streichen Sie die Aufgabe auf dem Papier durch, wählen Sie eine Aufgabe aus, die keine ausgehenden Fehler enthält (keine bekannten Abhängigkeiten), und kehren Sie zu 2 zurück.

Auf diese Weise erhalten Sie in kurzen Abständen eine funktionierende Codebasis. Hier können Sie auch Änderungen aus dem Rest des Teams zusammenführen. Und Sie haben eine visuelle Darstellung dessen, was Sie wissen, was Sie noch zu tun haben. Dies hilft bei der Entscheidung, ob Sie mit dem Endevour fortfahren möchten oder ob Sie es beenden sollten.

Jens Schauder
quelle
2

Refactoring ist eine strukturierte Disziplin, die sich von der Bereinigung von Code nach Belieben unterscheidet. Vor dem Start müssen Unit-Tests geschrieben werden, und jeder Schritt sollte aus einer bestimmten Transformation bestehen, von der Sie wissen, dass sie keine Änderungen an der Funktionalität vornehmen sollte. Die Komponententests sollten nach jeder Änderung bestanden werden.

Natürlich werden Sie während des Umgestaltungsprozesses feststellen, dass Änderungen vorgenommen werden müssen, die zu Beschädigungen führen können. Versuchen Sie in diesem Fall, ein Kompatibilitäts-Shim für die alte Schnittstelle zu implementieren, die das neue Framework verwendet. Theoretisch sollte das System weiterhin wie bisher funktionieren und die Komponententests sollten bestanden werden. Sie können das Kompatibilitäts-Shim als veraltete Schnittstelle markieren und zu einem geeigneteren Zeitpunkt bereinigen.

200_erfolg
quelle
2

... Ich habe das Projekt überarbeitet, um die Funktion hinzuzufügen.

Wie @Jules sagte, sind Refactoring und das Hinzufügen von Funktionen zwei sehr unterschiedliche Dinge.

  • Beim Refactoring geht es darum, die Programmstruktur zu ändern, ohne das Verhalten zu ändern.
  • Das Hinzufügen eines Features hingegen verstärkt sein Verhalten.

... aber in der Tat müssen Sie manchmal das Innenleben ändern, um Ihre Inhalte hinzuzufügen, aber ich würde es eher als Modifizieren als als Umgestalten bezeichnen.

Ich musste eine geringfügige Änderung an der Benutzeroberfläche vornehmen, um sie anzupassen

Dort wird es chaotisch. Schnittstellen sind als Grenzen gedacht, um die Implementierung von ihrer Verwendung zu isolieren. Sobald Sie Schnittstellen berühren, muss auch alles auf beiden Seiten (Implementierung oder Verwendung) geändert werden. Dies kann sich so weit ausbreiten, wie Sie es erlebt haben.

Dann kann die konsumierende Klasse nicht mit ihrer aktuellen Schnittstelle in Bezug auf die neue implementiert werden, sodass sie auch eine neue Schnittstelle benötigt.

Dass eine Schnittstelle eine Änderung erfordert, hört sich gut an ... dass sie sich auf eine andere Schnittstelle ausbreitet, bedeutet, dass sich die Änderungen noch weiter ausbreiten. Es hört sich so an, als ob irgendeine Form von Eingabe / Daten erforderlich wäre, um die Kette entlang zu fließen. Ist das der Fall?


Ihr Vortrag ist sehr abstrakt, daher ist es schwierig, dies herauszufinden. Ein Beispiel wäre sehr hilfreich. Normalerweise sollten die Schnittstellen ziemlich stabil und unabhängig voneinander sein, so dass es möglich ist, einen Teil des Systems zu ändern, ohne den Rest zu beschädigen ... dank der Schnittstellen.

... eigentlich sind gerade gute Schnittstellen der beste Weg, um kaskadierende Code-Änderungen zu vermeiden . ;)

dagnelies
quelle
-1

Ich denke, Sie können es normalerweise nicht, wenn Sie nicht bereit sind, die Dinge so zu belassen, wie sie sind. In Situationen wie Ihrer ist es jedoch meiner Meinung nach besser, das Team zu informieren und ihnen mitzuteilen, warum einige Umgestaltungen vorgenommen werden sollten, um eine gesunde Entwicklung fortzusetzen. Ich würde nicht einfach alles selbst reparieren. Ich würde darüber in Scrum-Meetings sprechen (vorausgesetzt, ihr habt sie) und mich systematisch mit anderen Entwicklern zusammensetzen.

Tarik
quelle
1
Dies scheint nichts Wesentliches über die Punkte zu bieten, die in den vorherigen 9 Antworten gemacht und erklärt wurden
Mücke
@gnat: Vielleicht nicht, aber die Antworten vereinfacht.
Tarik