Ich habe eine große Methode, die 3 Aufgaben erledigt, von denen jede in eine separate Funktion extrahiert werden kann. Wenn ich für jede dieser Aufgaben zusätzliche Funktionen erstelle, verbessert oder verschlechtert dies meinen Code und warum?
Offensichtlich werden weniger Codezeilen in der Hauptfunktion erstellt, aber es werden zusätzliche Funktionsdeklarationen vorhanden sein, sodass meine Klasse zusätzliche Methoden hat, von denen ich glaube, dass sie nicht gut sind, weil sie die Klasse komplexer machen.
Sollte ich das tun, bevor ich den gesamten Code geschrieben habe, oder sollte ich es so lange belassen, bis alles erledigt ist, und dann Funktionen extrahieren?
java
design-patterns
Dhblah
quelle
quelle
Antworten:
Dies ist ein Buch, auf das ich oft verweise, aber ich gehe noch einmal: Robert C. Martins Clean Code , Kapitel 3, "Funktionen".
Lesen Sie lieber eine Funktion mit +150 Zeilen oder eine Funktion, die 3 +50 Zeilenfunktionen aufruft? Ich denke, ich bevorzuge die zweite Option.
Ja , es verbessert Ihren Code in dem Sinne, dass er "lesbarer" wird. Machen Sie Funktionen, die eine und nur eine Sache ausführen, werden sie leichter zu warten und einen Testfall zu erstellen.
Auch eine sehr wichtige Sache, die ich mit dem oben genannten Buch gelernt habe: Wählen Sie gute und genaue Namen für Ihre Funktionen. Je wichtiger die Funktion ist, desto genauer sollte der Name sein. Machen Sie sich keine Sorgen über die Länge des Namens. Wenn er aufgerufen werden muss
FunctionThatDoesThisOneParticularThingOnly
, benennen Sie ihn so.Schreiben Sie einen oder mehrere Testfälle, bevor Sie Ihren Refactor durchführen. Stellen Sie sicher, dass sie funktionieren. Sobald Sie mit Ihrem Refactoring fertig sind, können Sie diese Testfälle starten, um sicherzustellen, dass der neue Code ordnungsgemäß funktioniert. Sie können zusätzliche "kleinere" Tests schreiben, um sicherzustellen, dass Ihre neuen Funktionen gut voneinander getrennt sind.
Und schließlich, und dies steht nicht im Widerspruch zu dem, was ich gerade geschrieben habe, fragen Sie sich, ob Sie dieses Refactoring wirklich durchführen müssen, und lesen Sie die Antworten unter " Wann ist ein Refactoring durchzuführen ?". (Suchen Sie auch nach SO Fragen zum Thema "Refactoring", es gibt mehr und die Antworten sind interessant zu lesen)
Wenn der Code bereits vorhanden ist und funktioniert und die Zeit für die nächste Version knapp ist, berühren Sie ihn nicht. Ansonsten denke ich, sollte man kleine Funktionen machen, wann immer möglich und als solche immer dann umgestalten, wenn etwas Zeit zur Verfügung steht, während sichergestellt wird, dass alles wie zuvor funktioniert (Testfälle).
quelle
Ja offensichtlich. Wenn es einfach ist, die verschiedenen "Aufgaben" einer einzelnen Funktion zu erkennen und zu trennen.
Aber es könnte Probleme damit geben:
quelle
Das Problem, das Sie aufwerfen, ist kein Problem der Codierung, Konventionen oder Codierungspraxis, sondern ein Problem der Lesbarkeit und der Art und Weise, wie Texteditoren den von Ihnen geschriebenen Code anzeigen. Das gleiche Problem taucht auch in der Post auf:
Ist es in Ordnung, lange Funktionen und Methoden in kleinere aufzuteilen, obwohl sie von nichts anderem aufgerufen werden?
Die Aufteilung einer Funktion in Unterfunktionen ist sinnvoll, wenn ein großes System mit der Absicht implementiert wird, die verschiedenen Funktionen, aus denen es bestehen soll, zusammenzufassen. Sie werden jedoch früher oder später mit einer Reihe großer Funktionen konfrontiert sein. Einige von ihnen sind unlesbar und schwierig zu pflegen, ob Sie sie als einzelne lange Funktionen behalten oder sie aufteilen, ist kleinere Funktionen. Dies gilt insbesondere für Funktionen, bei denen die von Ihnen ausgeführten Vorgänge an keiner anderen Stelle Ihres Systems erforderlich sind. Lassen Sie uns eine solch lange Funktion aufgreifen und sie in einer breiteren Sicht betrachten.
Profi:
Contra:
Stellen wir uns nun vor, die lange Funktion in mehrere Unterfunktionen aufzuteilen und sie mit einer breiteren Perspektive zu betrachten.
Profi:
Contra:
Beide Lösungen haben Vor- und Nachteile. Die beste Lösung wäre, Editoren zu haben, die es erlauben, jeden Funktionsaufruf in seinen Inhalt zu erweitern, inline und für die gesamte Tiefe. Daher ist die Aufteilung von Funktionen in Unterfunktionen die einzig beste Lösung.
quelle
Für mich gibt es vier Gründe, Codeblöcke in Funktionen zu extrahieren:
Sie verwenden es wieder : Sie haben gerade einen Codeblock in die Zwischenablage kopiert. Anstatt es einfach einzufügen, fügen Sie es in eine Funktion ein und ersetzen Sie den Block durch einen Funktionsaufruf auf beiden Seiten. Wenn Sie also diesen Codeblock ändern müssen, müssen Sie nur diese einzelne Funktion ändern, anstatt den Code an mehreren Stellen zu ändern. Wenn Sie also einen Codeblock kopieren, müssen Sie eine Funktion erstellen.
Es ist ein Rückruf : Es ist eine Ereignisbehandlungsroutine oder eine Art Benutzercode, den eine Bibliothek oder ein Framework aufruft. (Ich kann mir das kaum vorstellen, ohne Funktionen zu machen.)
Sie glauben, dass es im aktuellen Projekt oder an einem anderen Ort wiederverwendet wird: Sie haben gerade einen Block geschrieben, der die längste gemeinsame Folge von zwei Arrays berechnet. Selbst wenn Ihr Programm diese Funktion nur einmal aufruft, würde ich glauben, dass ich diese Funktion irgendwann auch in anderen Projekten benötigen werde.
Sie möchten selbstdokumentierenden Code : Anstatt also eine Kommentarzeile über einen Codeblock zu schreiben, in der zusammengefasst wird, was er tut, extrahieren Sie das Ganze in eine Funktion und benennen es so, wie Sie es in einen Kommentar schreiben würden. Obwohl ich kein Fan davon bin, weil ich gerne den Namen des verwendeten Algorithmus ausschreibe, den Grund, warum ich diesen Algorithmus gewählt habe, usw. Die Funktionsnamen wären dann zu lang ...
quelle
Ich bin sicher, Sie haben den Rat gehört, dass Variablen so eng wie möglich abgegrenzt werden sollten, und ich hoffe, Sie stimmen dem zu. Nun, Funktionen sind Container des Gültigkeitsbereichs, und in kleineren Funktionen ist der Gültigkeitsbereich der lokalen Variablen kleiner. Es ist viel klarer, wie und wann sie verwendet werden sollen, und es ist schwieriger, sie in der falschen Reihenfolge oder vor der Initialisierung zu verwenden.
Funktionen sind auch Container des logischen Flusses. Es gibt nur einen Weg hinein, die Auswege sind klar gekennzeichnet, und wenn die Funktion kurz genug ist, sollten die internen Flüsse offensichtlich sein. Dies führt zu einer Verringerung der zyklomatischen Komplexität, was eine zuverlässige Möglichkeit darstellt, die Fehlerrate zu verringern.
quelle
Nebenbei: Ich habe dies als Antwort auf Dallins Frage geschrieben (jetzt geschlossen), aber ich denke immer noch, dass es für jemanden hilfreich sein könnte
Ich denke, dass der Grund für die Zerstäubung von Funktionen 2-fach ist und wie @jozefg erwähnt, von der verwendeten Sprache abhängt.
Trennung von Bedenken
Der Hauptgrund dafür ist, verschiedene Codeteile getrennt zu halten, sodass jeder Codeblock, der nicht direkt zum gewünschten Ergebnis / zur gewünschten Absicht der Funktion beiträgt, ein separates Problem darstellt und extrahiert werden kann.
Angenommen, Sie haben eine Hintergrundaufgabe, die auch einen Fortschrittsbalken aktualisiert. Die Fortschrittsbalkenaktualisierung steht nicht in direktem Zusammenhang mit der ausgeführten Aufgabe. Sie sollte daher extrahiert werden, auch wenn dies der einzige Code ist, der den Fortschrittsbalken verwendet.
Angenommen, Sie haben in JavaScript eine Funktion getMyData (), die 1) eine Soap-Nachricht aus Parametern erstellt, 2) eine Dienstreferenz initialisiert, 3) den Dienst mit der Soap-Nachricht aufruft, 4) das Ergebnis analysiert, 5) das Ergebnis zurückgibt. Scheint vernünftig, ich habe genau diese Funktion viele Male geschrieben - aber das könnte wirklich in 3 private Funktionen aufgeteilt werden, einschließlich Code für 3 & 5 (falls das der Fall ist), da keiner der anderen Codes direkt für den Erhalt von Daten vom Service verantwortlich ist .
Verbessertes Debugging
Wenn Sie vollständig atomare Funktionen haben, wird Ihr Stack-Trace zu einer Aufgabenliste, die den gesamten erfolgreich ausgeführten Code auflistet, dh:
Es wäre viel interessanter, herauszufinden, dass beim Abrufen der Daten ein Fehler aufgetreten ist. Einige Tools sind jedoch noch nützlicher für das Debuggen detaillierter Anrufbäume, z. B. Microsoft Debugger Canvas .
Ich verstehe auch Ihre Bedenken, dass es schwierig sein kann, auf diese Weise geschriebenem Code zu folgen, da Sie am Ende des Tages eine Funktionsreihenfolge in einer einzelnen Datei auswählen müssen, da Ihr Aufrufbaum dann viel komplexer wäre . Aber wenn Funktionen gut benannt sind (Intellisense erlaubt mir, 3-4 Wörter in Groß- und Kleinschreibung in einer beliebigen Funktion zu verwenden, ohne mich zu verlangsamen) und mit einer öffentlichen Schnittstelle am Anfang der Datei strukturiert, liest sich Ihr Code wie ein Pseudocode, der Dies ist bei weitem der einfachste Weg, eine Codebasis auf hohem Niveau zu verstehen.
Zu Ihrer Information: Dies ist eines der Dinge, bei denen es keinen Sinn macht, Code atomar zu halten, es sei denn, Sie stimmen unbarmherzig damit überein, IMHO, was ich nicht bin.
quelle