Was ist Refactoring und was ändert nur Code?

79

Ich weiß, dass Refactoring "die Struktur eines Programms so ändert, dass die Funktionalität nicht geändert wird". Ich habe mit einigen der Leute gesprochen, mit denen ich an meinem Abschlussprojekt an der Universität zusammenarbeite, und ich war überrascht, dass sie eine viel expansivere (aus Mangel an einem besseren Wort) Sicht auf Refactoring haben.

Ich betrachte Refactoring als Dinge wie das Extrahieren von Methoden und das Umbenennen von Klassen. Sie schlugen auch Dinge wie das Ändern von Datenstrukturen (wie Java LinkedListin ein ArrayList), das Ändern von Algorithmen (Verwenden der Zusammenführungssortierung anstelle der Blasensortierung) und sogar das Umschreiben großer Codestücke als Refactoring vor.

Ich war mir ziemlich sicher, dass sie falsch lagen, aber ich konnte keinen guten Grund angeben, warum, weil das, was sie vorschlugen, das Programm geändert (und vermutlich verbessert) hat, ohne sein Verhalten zu ändern. Habe ich recht und was noch wichtiger ist, warum?

David Johnstone
quelle

Antworten:

75

Martin Fowlers "Refactoring: Verbesserung des Designs vorhandenen Codes" ist vielleicht DIE Referenz:

Refactoring ist eine kontrollierte Technik zur Verbesserung des Designs einer vorhandenen Codebasis. Seine Essenz besteht darin, eine Reihe kleiner verhaltenserhaltender Transformationen anzuwenden, von denen jede "zu klein ist, um es wert zu sein, getan zu werden". Der kumulative Effekt jeder dieser Transformationen ist jedoch ziemlich signifikant. Indem Sie sie in kleinen Schritten ausführen, verringern Sie das Risiko von Fehlern. Sie vermeiden auch, dass das System während der Umstrukturierung beschädigt wird. Auf diese Weise können Sie ein System über einen längeren Zeitraum schrittweise umgestalten.

Refactoring geht Hand in Hand mit Unit-Tests. Schreiben Sie Tests, bevor Sie den Refactor durchführen, und dann haben Sie ein Vertrauensniveau in das Refactoring (proportional zur Abdeckung der Tests).

Eine gute Referenz ist: Informationen zum Refactoring

Mitch Wheat
quelle
16
Das Fowler-Zitat ist natürlich relevant, und ja, es geht Hand in Hand mit Unit-Tests ... Aber beantwortet dies wirklich die gestellten Fragen: Werden in den genannten Beispielen Refactoring oder nur das Ändern von Code erwähnt? Wer hat Recht, das OP oder seine Kollegen und warum?
Jonik
34

Fowler zieht eine klare Linie zwischen Änderungen am Code, die sich auf das Verhalten auswirken, und solchen, die sich nicht auf das Verhalten auswirken. Er nennt diejenigen, die dies nicht tun, "Refactoring". Dies ist eine wichtige Unterscheidung, denn wenn wir unsere Arbeit in Refactoring- und Nicht-Refactoring-Code-Modifikationsaktivitäten unterteilen (Fowler nennt es "verschiedene Hüte tragen"), können wir verschiedene, zielgerichtete Techniken anwenden.

Wenn wir ein Refactoring oder eine verhaltenserhaltende Codeänderung vornehmen:

  • Alle unsere Unit-Tests sollten vor und nach der Änderung bestanden werden
  • Wir sollten keine Tests ändern oder neue schreiben müssen
  • Wir erwarten saubereren Code, wenn wir fertig sind
  • Wir erwarten kein neues Verhalten

Wenn wir eine verhaltensverändernde Codeänderung vornehmen:

  • Wir erwarten neues Verhalten
  • wir sollten neue Tests schreiben
  • Wenn wir fertig sind, erhalten wir möglicherweise schmutzigeren Code (und sollten ihn dann umgestalten).

Wenn wir diese Unterscheidung aus den Augen verlieren, sind unsere Erwartungen für eine bestimmte Code-Änderungsaufgabe durcheinander und komplex oder jedenfalls verwirrter und komplexer, als wenn wir uns dessen bewusst sind. Deshalb sind das Wort und seine Bedeutung wichtig.

Carl Manaster
quelle
3
+1 genau. Besonders die Begründung, die Sie geben; die durcheinandergebrachten Erwartungen. Als ich meine eigene Antwort schrieb,
dachte
18

Um meine Ansicht zu geben:

Kleine, inkrementelle Änderungen, die den Code in einem besseren Zustand belassen, als er gefunden wurde

Auf jeden Fall Ja: "Kosmetische" Änderungen, die nicht direkt mit Funktionen zusammenhängen (dh sie können nicht als Änderungsanforderung abgerechnet werden).

Auf jeden Fall Nein: Das Umschreiben großer Teile verstößt eindeutig gegen den "kleinen, inkrementellen" Teil. Refactoring wird oft als das Gegenteil eines Umschreibens verwendet: Anstatt es erneut zu tun, verbessern Sie das vorhandene.

Auf jeden Fall vielleicht: Das Ersetzen von Datenstrukturen und Algorithmen ist ein Grenzfall. Der entscheidende Unterschied hier IMO sind die kleinen Schritte: Seien Sie bereit zu liefern, seien Sie bereit, an einem anderen Fall zu arbeiten.


Beispiel: Stellen Sie sich vor, Sie haben ein Report Randomizer-Modul, das durch die Verwendung eines Vektors verlangsamt wird. Sie haben profiliert, dass Vektoreinfügungen der Engpass sind, aber leider ist das Modul an vielen Stellen auf zusammenhängenden Speicher angewiesen, sodass bei Verwendung einer Liste die Dinge stillschweigend kaputt gehen.

Das Umschreiben würde bedeuten, dass das Modul ein besseres und schnelleres Gebäude von Grund auf weggeworfen wird, indem nur einige Teile aus dem alten ausgewählt werden. Oder schreiben Sie einen neuen Kern und fügen Sie ihn dann in den vorhandenen Dialog ein.

Refactoring würde bedeuten, kleine Schritte zu unternehmen, um die Zeigerarithmetik zu entfernen, so dass der Schalter. Vielleicht erstellen Sie sogar eine Dienstprogrammfunktion, die die Zeigerarithmetik umschließt, die direkte Zeigermanipulation durch Aufrufe dieser Funktion ersetzt und dann zu einem Iterator wechselt, sodass sich der Compiler über Stellen beschwert, an denen die Zeigerarithmetik noch verwendet wird, und dann zu a listwechselt und dann die entfernt Ultilitätsfunktion.


Die Idee dahinter ist, dass Code von selbst schlechter wird. Wenn Sie Fehler beheben und Funktionen hinzufügen, nimmt die Qualität in kleinen Schritten ab - die Bedeutung einer Variablen ändert sich geringfügig, eine Funktion erhält einen zusätzlichen Parameter, der die Isolation aufhebt, eine Schleife wird etwas zu komplex usw. Keiner dieser Fehler ist ein echter Fehler Geben Sie keine Zeilenanzahl an, die die Schleife zu komplex macht, aber Sie beeinträchtigen die Lesbarkeit und Wartung.

Ebenso sind das Ändern eines Variablennamens oder das Extrahieren einer Funktion keine konkreten Verbesserungen für sich. Aber insgesamt bekämpfen sie die langsame Erosion.

Wie eine Kieselwand, an der man jeden Tag zu Boden fällt. Und jeden Tag nimmt ein Passant es auf und legt es zurück.

Peterchen
quelle
12

In Anbetracht der Definition von Martin Fowler

Refactoring ist eine disziplinierte Technik zur Umstrukturierung eines vorhandenen Code-Körpers, bei der die interne Struktur geändert wird, ohne das externe Verhalten zu ändern.

... Ich denke du hast eindeutig recht.

Sie schlugen auch Dinge wie das Ändern von Datenstrukturen (wie eine Java LinkedList in eine ArrayList), das Ändern von Algorithmen (Verwenden der Zusammenführungssortierung anstelle der Blasensortierung) und sogar das Umschreiben großer Codestücke als Refactoring vor.

Das Ändern eines Algorithmus auf etwas viel schnelleres ist offensichtlich kein Refactoring, da sich das externe Verhalten ändert! (Andererseits, wenn der Effekt nie spürbar ist, könnte man es vielleicht doch Refactoring nennen - und auch vorzeitige Optimierung. :-)

Dies ist ein Liebling von mir; Es ist ärgerlich, wenn Leute den Begriff schlampig verwenden - ich bin sogar auf einige gestoßen, die gelegentlich Refactoring für grundsätzlich jede Art von Änderung oder Korrektur verwenden. Ja, es ist ein hippes und cooles Schlagwort und alles, aber es ist nichts Falsches an einfachen alten Begriffen wie Ändern , Umschreiben oder Leistungsverbesserung . Wir sollten diese gegebenenfalls verwenden und Refactoring für Fälle reservieren, in denen Sie wirklich nur die interne Struktur Ihrer Software verbessern. Insbesondere in einem Entwicklungsteam ist es wichtig , eine gemeinsame Sprache für die genaue Diskussion Ihrer Arbeit zu haben.

Jonik
quelle
3
Ich bin nicht sicher, ob das Beschleunigen von Code eine Änderung des externen Verhaltens darstellt ...
GalacticCowboy
2
Ja, ich verstehe Ihren Standpunkt, ich denke, es hängt davon ab, aus welchem ​​Blickwinkel Sie ihn betrachten. :) Auf jeden Fall, IMO, sollten Sie beim Programmieren darauf achten, welchen "Hut" Sie in jedem Moment tragen, dh was genau Sie erreichen möchten. Mit anderen Worten, Sie sollten sich bewusst trennen, wenn Sie eine Funktion hinzufügen / korrigieren, wenn Sie ein Refactoring durchführen und wenn Sie die Leistung optimieren (verbessern). IIRC, Fowler spricht auch darüber in seinem endgültigen Buch über Refactoring.
Jonik
1
Für den externen Änderungsteil könnten wir umformulieren, um zu sagen: [...] ohne sein externes Verhalten zu ändern, wenn das Verhalten von Bedeutung ist. Wenn die Leistung in beiden Fällen wichtig ist (schneller oder langsamer), führen Sie keine Änderungen durch, die sich im Rahmen Ihrer "Refactoring" -Phase auf sie auswirken können.
Loki
10

Wenn sich die Schnittstelle zu einem Teil des Codes ändert, halte ich das für mehr als Refactoring.

Der typische Fall für Refactoring ist

  • "Oh, alle meine Unit-Tests laufen, aber ich denke, mein Code könnte sauberer gemacht werden."
  • Ändern Sie den Code, um besser lesbar / klarer / effizienter zu sein
  • Führen Sie Unit-Tests erneut durch (ohne die Tests zu ändern) und überprüfen Sie, ob sie noch funktionieren

Dies bedeutet, dass sich der Begriff Refactoring auf die von Ihnen diskutierte Schnittstelle bezieht. Das heißt, Sie könnten den Code hinter einer Schnittstelle umgestalten, während Sie den Code einer anderen Schnittstelle auf einer niedrigeren Ebene ausführlicher ändern (möglicherweise sorgt diese Unterscheidung für die Verwirrung zwischen Ihnen und Ihren Kollegen hier?).

DanSingerman
quelle
7

Ich denke, Sie haben Recht, aber über die Bedeutung eines Wortes zu streiten ist nicht besonders interessant oder produktiv.

cbp
quelle
6
Normalerweise stimme ich dem zu, aber die Diskussion kam auf, als wir Dokumente überprüften, die wir geschrieben hatten, und ich denke, es ist gut, wenn wir über dasselbe reden, wenn wir dieselben Wörter verwenden.
David Johnstone
4

http://en.wikipedia.org/wiki/Code_refactoring

Beim Code-Refactoring wird die interne Struktur eines Computerprogramms geändert, ohne das externe Funktionsverhalten oder die vorhandene Funktionalität zu ändern, um die internen nichtfunktionalen Eigenschaften der Software zu verbessern, beispielsweise um die Lesbarkeit des Codes zu verbessern, die Codestruktur zu vereinfachen und den Code zu ändern sich an ein bestimmtes Programmierparadigma zu halten, die Wartbarkeit zu verbessern, die Leistung zu verbessern oder die Erweiterbarkeit zu verbessern.

Ich bin damit einverstanden, dass das Refactoring von Code das Brechen von vorhandenem Code beinhaltet. Stellen Sie einfach sicher, dass Sie Unit-Tests haben, damit Sie keine Fehler einführen und der Rest des Codes kompiliert wird. Die Verwendung von Refactoring-Tools wie Resharper für C # macht dies so einfach!

  • Code verständlicher machen
  • Code bereinigen und aufräumen
  • Code entfernen! Redundanter, nicht verwendeter Code und Kommentare sollten gelöscht werden
  • Verbessernde Leistung
  • Etwas allgemeineres machen. Beginnen Sie mit der einfachsten Sache und überarbeiten Sie sie dann, um das Testen / Isolieren oder Generieren zu vereinfachen, damit sie durch Polymorphismus auf unterschiedliche Weise funktionieren kann
  • Halten Sie den Code trocken - Wiederholen Sie sich nicht, daher kann eine Refactoring-Sitzung das Wiederholen von Code in eine einzelne Komponente / Klasse / Modul umfassen.
superlogisch
quelle
3

Ich bin anderer Meinung :

In der Softwareentwicklung bedeutet "Refactoring" des Quellcodes, ihn zu verbessern, ohne seine Gesamtergebnisse zu ändern [...]

Sie kennen bereits die genaueren Begriffe, die für Teilmengen des Refactorings verwendet werden, und ja, es ist ein sehr allgemeiner Begriff.

l0b0
quelle
1

Ich denke, dass niemand von einer zu starken Definition des Begriffs „Refactoring“ profitieren kann. Die Grenze zwischen Ihrer Wahrnehmung und Ihren Kollegen ist verschwommen und kann abhängig von vielen Fakten näher an ihrer oder Ihrer Ansicht liegen. Da es dynamisch ist, versuchen wir es zu definieren. Definieren Sie zunächst die Grenzen des Systems oder Subsystems, das Sie umgestalten möchten.

Wenn es sich um eine Methode handelt, behalten Sie den Namen, die Eingabeargumente, den Typ des zurückgegebenen Werts und möglicherweise die auslösenden Anweisungen bei. Wenden Sie alle Änderungen innerhalb der Methode an, ohne zu ändern, wie sie außerhalb angezeigt wird.

Wenn Sie eine Klasse umgestalten, ihre öffentliche API korrigieren und Umbenennungsvariablen verwenden, ändern Extraktionsmethoden und alle anderen verfügbaren Techniken die Klasse so, dass sie lesbarer und / oder leistungsfähiger ist.

Wenn der Teil des Codes, den Sie umgestalten, ein Paket oder ein Modul ist, führen Sie das darin enthaltene Refactoring möglicherweise um, indem Sie Klassen umbenennen, löschen, Schnittstellen einführen, Code in Super- / Unterklassen verschieben / ziehen.

Boris Pavlović
quelle
0

Refactoring = Verbesserung der nicht funktionalen Anforderungen unter Beibehaltung der funktionalen Anforderungen.

Nicht funktionale Anforderungen = Modularität, Testbarkeit, Wartbarkeit, Lesbarkeit, Trennung von Bedenken, Liskov-Prinzipien und so weiter ...

EnzoVici
quelle