Laut Martin Fowler ist Code-Refactoring (Hervorhebung von mir):
Refactoring ist eine disziplinierte Technik zur Umstrukturierung eines vorhandenen Codebestandes, bei der die interne Struktur geändert wird, ohne dass das externe Verhalten geändert wird . Sein Herz ist eine Reihe von kleinen Verhaltensweisen, die Transformationen bewahren. Jede Transformation (als "Refactoring" bezeichnet) hat nur eine geringe Wirkung, eine Abfolge von Transformationen kann jedoch zu einer erheblichen Umstrukturierung führen. Da jedes Refactoring klein ist, ist es weniger wahrscheinlich, dass es schief geht. Das System wird auch nach jeder kleinen Umgestaltung voll funktionsfähig gehalten, wodurch die Wahrscheinlichkeit verringert wird, dass ein System während der Umstrukturierung ernsthaft beschädigt wird.
Was ist in diesem Zusammenhang "äußeres Verhalten"? Wenn ich zum Beispiel das Umgestalten von Verschiebungsmethoden anwende und eine Methode in eine andere Klasse verschiebe, sieht es so aus, als würde ich das externe Verhalten ändern, nicht wahr?
Ich bin also daran interessiert herauszufinden, an welchem Punkt eine Veränderung aufhört, ein Refaktor zu sein, und etwas mehr wird. Der Begriff "Refactoring" kann für größere Änderungen missbraucht werden: Gibt es ein anderes Wort dafür?
Aktualisieren. Viele interessante Antworten zur Benutzeroberfläche, aber würde das Umgestalten von Methoden die Benutzeroberfläche nicht ändern?
quelle
Antworten:
"Extern" bedeutet in diesem Zusammenhang "für Benutzer beobachtbar". Benutzer können im Falle einer Anwendung Menschen sein, oder andere Programme im Falle einer öffentlichen API.
Wenn Sie also die Methode M von Klasse A in Klasse B verschieben und sich beide Klassen tief in einer Anwendung befinden und kein Benutzer aufgrund der Änderung eine Änderung im Verhalten der App feststellen kann, können Sie sie zu Recht als Refactoring bezeichnen.
Wenn OTOH, ein anderes übergeordnetes Subsystem / eine andere übergeordnete Komponente, sein Verhalten ändert oder aufgrund der Änderung zusammenbricht, ist dies tatsächlich (normalerweise) für Benutzer (oder zumindest für Sysadmins, die Protokolle überprüfen) beobachtbar. Oder wenn Ihre Klassen Teil einer öffentlichen API waren, gibt es möglicherweise Code von Drittanbietern, der davon abhängt, dass M Teil der Klasse A und nicht B ist. In beiden Fällen handelt es sich also nicht um ein Refactoring im eigentlichen Sinne.
In der Tat ist es eine traurige, aber zu erwartende Folge, dass das Refactoring in Mode kommt. Entwickler arbeiten seit Ewigkeiten ad hoc an Code, und es ist mit Sicherheit einfacher, ein neues Schlagwort zu lernen, als tief verwurzelte Gewohnheiten zu analysieren und zu ändern.
Ich würde es Redesign nennen .
Aktualisieren
Von was? Die spezifischen Klassen, ja. Aber sind diese Klassen in irgendeiner Weise für die Außenwelt direkt sichtbar? Wenn nicht - weil sie sich in Ihrem Programm befinden und nicht Teil der externen Schnittstelle (API / GUI) des Programms sind - ist keine dort vorgenommene Änderung für externe Parteien erkennbar (es sei denn, die Änderung bringt natürlich etwas kaputt).
Ich habe das Gefühl, dass es darüber hinaus eine tiefere Frage gibt: Existiert eine bestimmte Klasse als eigenständige Einheit? In den meisten Fällen lautet die Antwort nein : Die Klasse existiert nur als Teil einer größeren Komponente, eines Ökosystems von Klassen und Objekten, ohne das sie nicht instanziiert werden kann und / oder unbrauchbar ist. Dieses Ökosystem umfasst nicht nur seine (direkten / indirekten) Abhängigkeiten, sondern auch andere Klassen / Objekte, die davon abhängen. Dies liegt daran, dass ohne diese übergeordneten Klassen die mit unserer Klasse verbundene Verantwortung für die Benutzer des Systems möglicherweise bedeutungslos / nutzlos ist.
ZB in unserem Projekt, das sich mit Mietwagen befasst, gibt es eine
Charge
Klasse. Diese Klasse ist für die Benutzer des Systems für sich genommen nutzlos, da Vermittler von Mietstationen und Kunden mit einer einzelnen Gebühr nicht viel anfangen können: Sie beschäftigen sich mit Mietvertragsverträgen als Ganzes (die eine Reihe verschiedener Arten von Gebühren enthalten). . Die Benutzer interessieren sich hauptsächlich für die Gesamtsumme dieser Gebühren, die sie am Ende zahlen sollen; Der Makler interessiert sich für die verschiedenen Vertragsoptionen, die Länge der Anmietung, die Fahrzeuggruppe, das Versicherungspaket, die ausgewählten zusätzlichen Gegenstände usw. usw., die (über ausgefeilte Geschäftsregeln) bestimmen, welche Gebühren anfallen und wie die Restzahlung berechnet wird von diesen. Und Ländervertreter / Business Analysten kümmern sich um die spezifischen Geschäftsregeln, ihre Synergien und Auswirkungen (auf das Einkommen des Unternehmens usw.).Kürzlich habe ich diese Klasse überarbeitet und die meisten ihrer Felder und Methoden umbenannt (um der Standard-Java-Namenskonvention zu folgen, die von unseren Vorgängern völlig vernachlässigt wurde). Ich plane auch weitere Refactorings zu ersetzen
String
undchar
Felder mit angemessenereenum
undboolean
Typen. All dies wird sicherlich die Oberfläche der Klasse verändern, aber (wenn ich meine Arbeit richtig mache) wird nichts davon für die Benutzer unserer App sichtbar. Keiner von ihnen kümmert sich darum, wie einzelne Gebühren dargestellt werden, obwohl sie den Begriff der Gebühr sicher kennen. Ich hätte als Beispiel hundert andere Klassen auswählen können, die kein Domänenkonzept repräsentieren, also für die Endbenutzer sogar konzeptionell unsichtbar sind, aber ich fand es interessanter, ein Beispiel auszuwählen, bei dem es zumindest eine gewisse Sichtbarkeit auf Konzeptebene gibt. Dies zeigt deutlich, dass Klassenschnittstellen (bestenfalls) nur Darstellungen von Domänenkonzepten sind, nicht die Realität *. Die Darstellung kann ohne Auswirkung auf das Konzept geändert werden. Und Benutzer haben nur das Konzept und verstehen es; es ist unsere aufgabe, das mapping zwischen konzept und darstellung vorzunehmen.* Und man kann leicht hinzufügen, dass das Domain-Modell, das unsere Klasse darstellt, selbst nur eine ungefähre Darstellung einer "echten Sache" ist ...
quelle
Extern bedeutet einfach Schnittstelle in ihrer wahren sprachlichen Bedeutung. Betrachten Sie für dieses Beispiel eine Kuh. Solange Sie etwas Gemüse füttern und Milch als Rückgabewert erhalten, ist es Ihnen egal, wie die inneren Organe funktionieren. Wenn Gott nun die inneren Organe der Kühe verändert, so dass ihr Blut eine blaue Farbe annimmt, solange sich der Eintritts- und der Austrittspunkt (Mund und Milch) nicht ändern, kann dies als Umgestaltung angesehen werden.
quelle
Refactoring war für mich am produktivsten / komfortabelsten, als die Grenzen durch Tests und / oder formale Spezifikationen festgelegt wurden.
Diese Grenzen sind so starr, dass ich mich sicher fühle, wenn ich sie gelegentlich überschreite, wird sie früh genug erkannt, sodass ich nicht viele Änderungen rückgängig machen muss, um sie wiederherzustellen. Auf der anderen Seite bieten diese genügend Spielraum, um den Code zu verbessern, ohne sich Gedanken über das Ändern irrelevanten Verhaltens machen zu müssen.
Mir gefällt vor allem, dass solche Grenzen sozusagen adaptiv sind . Ich meine, 1) Ich nehme die Änderung vor und überprüfe, ob sie den Spezifikationen / Tests entspricht. Dann 2) wird es an die Qualitätssicherung oder an den Benutzer weitergegeben. Beachten Sie, dass dies möglicherweise immer noch fehlschlägt, da in den Spezifikationen / Tests etwas fehlt. OK, wenn 3a) der Test bestanden ist, bin ich fertig, okay. Andernfalls, wenn 3b) das Testen fehlschlägt, I 4) die Änderung rückgängig machen und 5) Tests hinzufügen oder die Spezifikation präzisieren, damit sich dieser Fehler beim nächsten Mal nicht wiederholt. Beachten Sie, dass ich unabhängig davon, ob der Test bestanden wurde oder nicht, einen Vorteil erhalte - entweder Code / Tests / Spezifikationen werden verbessert -, und meine Bemühungen gehen nicht in völlige Verschwendung über.
Was andere Grenzen angeht - bisher hatte ich nicht viel Glück.
"Für Benutzer beobachtbar" ist eine sichere Wette, wenn man eine Disziplin hat, der man folgen muss - was für mich immer viel Aufwand bei der Analyse bestehender / der Erstellung neuer Tests mit sich brachte -, vielleicht zu viel Aufwand. Eine andere Sache, die ich an diesem Ansatz nicht mag, ist, dass es sich als zu restriktiv herausstellen kann, ihn blind zu befolgen. - Diese Änderung ist nicht zulässig, da das Laden der Daten 3 Sekunden statt 2 Sekunden dauert. - Wie wäre es, wenn Sie mit Benutzern / UX-Experten prüfen, ob dies relevant ist oder nicht? - In keinem Fall ist eine Änderung des beobachtbaren Verhaltens untersagt. Sicher? Sie wetten! produktiv? nicht wirklich.
Eine andere, die ich versucht habe, ist, die Codelogik beizubehalten (wie ich sie beim Lesen verstehe). Abgesehen von den elementarsten (und normalerweise nicht sehr fruchtbaren) Änderungen war diese immer eine Dose Würmer ... oder sollte ich eine Dose Wanzen sagen? Ich meine Regressionsfehler. Bei der Arbeit mit Spaghetti-Code ist es einfach zu einfach, etwas Wichtiges zu kaputt zu machen.
quelle
Das Refactoring- Buch enthält die aussagekräftige Botschaft, dass Sie Refactoring nur durchführen können , wenn Sie über eine Einheitentestabdeckung verfügen .
Sie könnten also sagen, dass Sie eine Umgestaltung vornehmen, solange Sie keinen Ihrer Komponententests brechen . Wenn Sie Tests brechen, refaktorieren Sie nicht mehr.
Aber wie wäre es mit so einfachen Umgestaltungen wie dem Ändern der Namen von Klassen oder Mitgliedern? Brechen sie nicht Tests?
Ja, und in jedem Fall müssen Sie überlegen, ob diese Unterbrechung von Bedeutung ist. Wenn Ihr SUT eine öffentliche API / ein öffentliches SDK ist, ist eine Umbenennung in der Tat eine bahnbrechende Änderung. Wenn nicht, ist es wahrscheinlich in Ordnung.
Bedenken Sie jedoch, dass Tests häufig unterbrochen werden, nicht weil Sie das Verhalten geändert haben, das Sie wirklich interessiert, sondern weil es sich bei den Tests um fragile Tests handelt .
quelle
Der beste Weg, um "externes Verhalten" in diesem Kontext zu definieren, können "Testfälle" sein.
Wenn Sie den Code umgestalten und die (vor dem Umgestalten definierten) Testfälle weiterhin bestehen, hat das Umgestalten das externe Verhalten nicht geändert. Wenn ein oder mehrere Testfälle fehlschlagen, haben Sie das externe Verhalten geändert.
Zumindest ist dies mein Verständnis der verschiedenen Bücher, die zu diesem Thema veröffentlicht wurden (z. B. Fowlers).
quelle
Die Grenze ist die Grenze zwischen den Entwicklern, Betreuern, Betreuern und Entwicklern, die das Projekt entwickeln, pflegen und unterstützen. Für die Außenwelt sieht das Verhalten also genauso aus, während sich die internen Strukturen hinter dem Verhalten geändert haben.
Es sollte also in Ordnung sein, Funktionen zwischen Klassen zu verschieben, sofern dies nicht die sind, die die Benutzer sehen.
Solange die Codeüberarbeitung das externe Verhalten nicht ändert, neue Funktionen hinzufügt oder vorhandene Funktionen entfernt, ist es in Ordnung, die Überarbeitung als Refactoring zu bezeichnen.
quelle
Bei allem Respekt müssen wir uns daran erinnern, dass die Benutzer einer Klasse nicht die Endbenutzer der Anwendungen sind, die mit der Klasse erstellt wurden, sondern die Klassen, die implementiert werden, indem die neu gestaltete Klasse entweder aufgerufen oder von ihr geerbt wird .
Wenn Sie sagen, dass sich das externe Verhalten nicht ändern sollte, bedeutet dies, dass sich die Klasse für die Benutzer genauso verhält wie zuvor. Es kann sein, dass die ursprüngliche (nicht überarbeitete) Implementierung eine einzelne Klasse war und die neue (überarbeitete) Implementierung eine oder mehrere Superklassen aufweist, auf denen die Klasse aufbaut, aber die Benutzer sehen nie das Innere (die Implementierung), in dem sie sich befinden siehe nur die Schnittstelle.
Wenn also eine Klasse eine Methode namens "doSomethingAmazing" hat, ist es für den Benutzer unerheblich, ob dies von der Klasse implementiert wird, auf die sie sich beziehen, oder von einer Superklasse, auf der diese Klasse aufgebaut ist. Für den Benutzer ist nur wichtig, dass das neue (überarbeitete) "doSomethingAmazing" dasselbe Ergebnis wie das alte (nicht überarbeitete) "doSomethingAmazing" liefert.
Was in vielen Fällen als Refactoring bezeichnet wird, ist jedoch kein echtes Refactoring, sondern möglicherweise eine Neuimplementierung, mit der der Code einfacher geändert werden kann, um neue Funktionen hinzuzufügen. In diesem späteren Fall der (Pseudo-) Umgestaltung bewirkt der neue (umgestaltete) Code also tatsächlich etwas anderes oder vielleicht mehr als den alten.
quelle
Unter "externem Verhalten" versteht er in erster Linie die öffentliche Schnittstelle, dies umfasst aber auch Ausgaben / Artefakte des Systems. (dh Sie haben eine Methode, die eine Datei generiert. Wenn Sie das Format der Datei ändern, ändert sich auch das externe Verhalten.)
e: Ich würde die "Verschiebungsmethode" als eine Änderung des externen Verhaltens betrachten. Denken Sie daran, dass Fowler über vorhandene Codebasen spricht, die in der freien Wildbahn veröffentlicht wurden. Abhängig von Ihrer Situation können Sie möglicherweise überprüfen, ob Ihre Änderung keine externen Kunden beeinträchtigt, und auf fröhliche Weise fortfahren.
e2: "Also, was ist das richtige Wort für Überarbeitungen, die das äußere Verhalten verändern?" - API Refactoring, Breaking Change, etc ... es ist immer noch Refactoring, es folgt einfach nicht den Best Practices für das Refactoring einer öffentlichen Schnittstelle, die bereits mit Clients in Kontakt steht.
quelle
"Move-Methode" ist eine Refactoring-Technik, keine Refactoring-Methode für sich. Ein Refactoring ist das Anwenden mehrerer Refactoring-Techniken auf Klassen. Wenn Sie sagen, dass ich "move method" auf eine Klasse angewendet habe, meinen Sie nicht "I refactored (the class)", sondern "I used a refactoring technique on this class". Refactoring bezieht sich in seiner reinsten Bedeutung auf das Design oder genauer auf einen Teil des Anwendungsdesigns, der als Black Box angesehen werden kann.
Man könnte sagen, dass "Refactoring", das im Kontext von Klassen verwendet wird, "Refactoring-Technik" bedeutet und somit "Move-Methode" nicht die Definition von "Refactoring-the-Process" verletzt. Auf der gleichen Seite werden durch "Refactoring" im Kontext des Designs keine vorhandenen Features im Code beschädigt, sondern nur das Design (was auch immer der Zweck ist).
Abschließend wird die in der Frage erwähnte "Grenze" überschritten, wenn Sie die Refactoring-Technik mit der Refactoring-Technik verwechseln (mix: D).
PS: Gute Frage übrigens.
quelle
Wenn Sie über Factoring-Zahlen sprechen, dann beschreiben Sie die Gruppe von ganzen Zahlen, die, wenn sie miteinander multipliziert werden, gleich der Startzahl sind. Wenn wir diese Definition für Factoring verwenden und sie auf den Programmierbegriff Refactor anwenden, würde Refactoring ein Programm in die kleinsten logischen Einheiten aufteilen, sodass bei Ausführung als Programm dieselbe Ausgabe (bei derselben Eingabe) erzeugt wird ) als ursprüngliches Programm.
quelle
Die Grenzen eines Refactorings sind spezifisch für ein bestimmtes Refactoring. Es kann einfach keine Antwort geben und umfasst alles. Ein Grund dafür ist, dass der Begriff Refactoring selbst nicht spezifisch ist.
Sie können umgestalten:
und ich bin sicher, mehrere andere Dinge.
Vielleicht könnte Refactor definiert werden als
quelle
Es gibt definitiv einige Grenzen, wie weit Sie umgestalten können. Siehe: Wann sind zwei Algorithmen gleich?
Gehen Sie also nicht zu weit, sonst haben Sie kein Vertrauen in das Ergebnis. Auf der anderen Seite können Sie erfahrungsgemäß häufig einen Algorithmus durch einen anderen ersetzen und die gleichen Antworten erhalten, manchmal sogar schneller. Das ist das Schöne daran, oder?
quelle
Sie können an „externe“ Bedeutung denken
Jede Änderung des Systems, die keine Auswirkungen auf Schweine hat, kann als Refactoring angesehen werden.
Das Ändern einer Klassenschnittstelle ist kein Problem, wenn die Klasse nur von einem einzelnen System verwendet wird, das von Ihrem Team erstellt und verwaltet wird. Wenn es sich bei der Klasse jedoch um eine öffentliche Klasse im .net-Framework handelt, die von jedem .net-Programmierer verwendet wird, ist dies eine ganz andere Angelegenheit.
quelle