35 Zeilen, 55 Zeilen, 100 Zeilen, 300 Zeilen? Wann sollten Sie anfangen, es auseinanderzubrechen? Ich frage, weil ich eine Funktion mit 60 Zeilen (einschließlich Kommentaren) habe und darüber nachgedacht habe, sie auseinanderzubrechen.
long_function(){ ... }
in:
small_function_1(){...}
small_function_2(){...}
small_function_3(){...}
Die Funktionen werden nicht außerhalb der Funktion long_function verwendet. Kleinere Funktionen bedeuten mehr Funktionsaufrufe usw.
Wann würden Sie eine Funktion in kleinere aufteilen? Warum?
- Methoden sollten nur eine logische Sache tun (über Funktionalität nachdenken)
- Sie sollten in der Lage sein, die Methode in einem einzigen Satz zu erklären
- Es sollte in die Höhe Ihres Displays passen
- Vermeiden Sie unnötigen Overhead (Kommentare, die auf das Offensichtliche hinweisen ...)
- Unit-Tests sind für kleine logische Funktionen einfacher
- Überprüfen Sie, ob ein Teil der Funktion von anderen Klassen oder Methoden wiederverwendet werden kann
- Vermeiden Sie eine übermäßige Kopplung zwischen den Klassen
- Vermeiden Sie tief verschachtelte Kontrollstrukturen
Vielen Dank an alle für die Antworten , bearbeite die Liste und stimme für die richtige Antwort. Ich werde diese auswählen;)
Ich überarbeite jetzt mit diesen Ideen im Hinterkopf :)
Antworten:
Es gibt keine wirklich festen Regeln dafür. Im Allgemeinen mag ich meine Methoden, um nur "eine Sache zu tun". Wenn es also darum geht, Daten zu erfassen, dann etwas mit diesen Daten zu tun und sie dann auf die Festplatte zu schreiben, würde ich das Erfassen und Schreiben in separate Methoden aufteilen, sodass meine "Haupt" -Methode nur das "etwas tun" enthält.
Das "etwas tun" könnte immer noch einige Zeilen umfassen, daher bin ich mir nicht sicher, ob eine Anzahl von Zeilen die richtige Metrik ist :)
Bearbeiten: Dies ist eine einzelne Codezeile, die ich letzte Woche per Post verschickt habe (um einen Punkt zu beweisen. Ich mache es mir nicht zur Gewohnheit :)) - Ich würde sicher nicht 50-60 dieser bösen Jungs in meiner Methode haben wollen : D.
quelle
Hier ist eine Liste von roten Fahnen (in keiner bestimmten Reihenfolge), die darauf hinweisen könnten, dass eine Funktion zu lang ist:
Tief verschachtelte Kontrollstrukturen : zB for-Schleifen 3 Ebenen tief oder sogar nur 2 Ebenen tief mit verschachtelten if-Anweisungen, die komplexe Bedingungen haben.
Zu viele zustandsdefinierende Parameter : Mit zustandsdefinierendem Parameter meine ich einen Funktionsparameter, der einen bestimmten Ausführungspfad durch die Funktion garantiert. Wenn Sie zu viele dieser Parameter erhalten, kommt es zu einer kombinatorischen Explosion von Ausführungspfaden (dies geschieht normalerweise zusammen mit # 1).
Logik, die bei anderen Methoden dupliziert wird : Eine schlechte Wiederverwendung von Code trägt erheblich zum monolithischen prozeduralen Code bei. Viele solcher logischen Duplikate können sehr subtil sein, aber wenn sie einmal berücksichtigt werden, kann das Endergebnis ein weitaus eleganteres Design sein.
Übermäßige Kopplung zwischen Klassen : Dieses Fehlen einer ordnungsgemäßen Kapselung führt dazu, dass Funktionen sich mit intimen Merkmalen anderer Klassen befassen und diese somit verlängern.
Unnötiger Overhead : Kommentare, die auf die offensichtlichen, tief verschachtelten Klassen, überflüssigen Getter und Setter für private verschachtelte Klassenvariablen und ungewöhnlich lange Funktions- / Variablennamen hinweisen, können syntaktisches Rauschen innerhalb verwandter Funktionen erzeugen, das letztendlich ihre Länge erhöht.
Ihr massives Display für Entwickler ist nicht groß genug, um es anzuzeigen : Tatsächlich sind die Displays von heute groß genug, dass eine Funktion, die sich irgendwo in der Nähe ihrer Höhe befindet, wahrscheinlich viel zu lang ist. Aber wenn es größer ist , ist dies eine rauchende Waffe, bei der etwas nicht stimmt.
Sie können nicht sofort die Zweck der Funktion bestimmen : Darüber hinaus , wenn Sie tatsächlich tun ihren Zweck bestimmen, wenn Sie diesen Zweck in einem einzigen Satz nicht zusammenfassen können oder passieren einen enormen Kopfschmerzen haben, soll dies ein Hinweis sein.
Zusammenfassend lässt sich sagen, dass monolithische Funktionen weitreichende Konsequenzen haben können und häufig ein Symptom für schwerwiegende Konstruktionsmängel sind. Immer wenn ich auf Code stoße , dessen Lesen eine absolute Freude ist , wird seine Eleganz sofort deutlich. Und raten Sie mal: Die Funktionen sind oft sehr kurz.
quelle
// fetch Foo's credentials where Bar is "uncomplete"
. Das ist mit ziemlicher Sicherheit ein Funktionsname und sollte entkoppelt werden. Möchte wahrscheinlich in etwas umgestaltet werden wie:Foo.fetchCredentialWhereBarUncomplete()
Ich denke, das Mantra "mach nur eins" auf dieser Seite hat eine große Einschränkung. Manchmal jongliert eine Sache mit vielen Variablen. Teilen Sie eine lange Funktion nicht in eine Reihe kleinerer Funktionen auf, wenn die kleineren Funktionen lange Parameterlisten haben. Dadurch wird aus einer einzelnen Funktion eine Reihe stark gekoppelter Funktionen ohne echten individuellen Wert.
quelle
Eine Funktion sollte nur eines tun. Wenn Sie viele kleine Dinge in einer Funktion tun, machen Sie jedes kleine Ding zu einer Funktion und rufen Sie diese Funktionen über die lange Funktion auf.
Was Sie wirklich nicht tun möchten, ist, alle 10 Zeilen Ihrer langen Funktion zu kopieren und in kurze Funktionen einzufügen (wie in Ihrem Beispiel vorgeschlagen).
quelle
Ich bin damit einverstanden, dass eine Funktion nur eines tun sollte, aber auf welcher Ebene ist das eine.
Wenn Ihre 60 Zeilen eine Sache erreichen (aus Sicht Ihres Programms) und die Teile, aus denen diese 60 Zeilen bestehen, von nichts anderem verwendet werden, sind 60 Zeilen in Ordnung.
Es hat keinen wirklichen Vorteil, es aufzubrechen, es sei denn, Sie können es in Betonstücke zerlegen, die für sich allein stehen. Die zu verwendende Metrik ist Funktionalität und keine Codezeilen.
Ich habe an vielen Programmen gearbeitet, bei denen die Autoren das Einzige auf ein extremes Niveau gebracht haben, und alles, was es letztendlich getan hat, war, es so aussehen zu lassen, als hätte jemand eine Granate zu einer Funktion / Methode genommen und sie in Dutzende nicht verbundener Teile gesprengt schwer zu folgen.
Wenn Sie Teile dieser Funktion herausziehen, müssen Sie auch berücksichtigen, ob Sie unnötigen Overhead hinzufügen und vermeiden, große Datenmengen zu übertragen.
Ich glaube, der entscheidende Punkt ist, die Wiederverwendbarkeit in dieser langen Funktion zu suchen und diese Teile herauszuziehen. Was Ihnen bleibt, ist die Funktion, ob sie 10, 20 oder 60 Zeilen lang ist.
quelle
60 Zeilen sind groß, aber nicht zu lang für eine Funktion. Wenn es auf einen Bildschirm in einem Editor passt, können Sie alles auf einmal sehen. Es kommt wirklich darauf an, was die Funktionen tun.
Warum ich eine Funktion auflösen kann:
quelle
DoThisAndThisAndAlsoThis
Funktion, aber mit viel Abstraktion, die Sie noch irgendwie benennen müssenMeine persönliche Heuristik ist, dass es zu lang ist, wenn ich das Ganze nicht sehen kann, ohne zu scrollen.
quelle
Größe ca. Sie Bildschirmgröße (also holen Sie sich einen großen Pivot-Breitbildschirm und drehen Sie ihn) ... :-)
Scherz beiseite, eine logische Sache pro Funktion.
Und das Positive ist, dass Unit-Tests mit kleinen logischen Funktionen, die eine Sache erledigen, sehr viel einfacher sind. Große Funktionen, die viele Dinge tun, sind schwerer zu überprüfen!
/ Johan
quelle
Faustregel: Wenn eine Funktion Codeblöcke enthält, die etwas bewirken, das etwas vom Rest des Codes getrennt ist, setzen Sie sie in eine separate Funktion. Beispiel:
Viel schöner:
Dieser Ansatz hat zwei Vorteile:
Wann immer Sie Adressen für eine bestimmte Postleitzahl abrufen müssen, können Sie die sofort verfügbare Funktion verwenden.
Wenn Sie die Funktion jemals wieder lesen müssen,
build_address_list_for_zip()
wissen Sie, was der erste Codeblock tun wird (er ruft Adressen für eine bestimmte Postleitzahl ab, zumindest können Sie dies aus dem Funktionsnamen ableiten). Wenn Sie den Abfragecode inline gelassen hätten, müssten Sie zuerst diesen Code analysieren.[Auf der anderen Seite (ich werde leugnen, dass ich Ihnen dies auch unter Folter gesagt habe): Wenn Sie viel über PHP-Optimierung lesen, könnten Sie auf die Idee kommen, die Anzahl der Funktionen so gering wie möglich zu halten, da Funktionsaufrufe sehr, sehr teuer in PHP. Das weiß ich nicht, da ich nie Benchmarks gemacht habe. In diesem Fall ist es wahrscheinlich besser, keine der Antworten auf Ihre Frage zu befolgen, wenn Ihre Anwendung sehr "leistungsempfindlich" ist ;-)]
quelle
Werfen Sie einen Blick auf McCabes Zyklomatik, in der er seinen Code in ein Diagramm aufteilt, in dem "Jeder Knoten im Diagramm einem Codeblock im Programm entspricht, in dem der Fluss sequentiell ist und die Bögen den im Programm aufgenommenen Zweigen entsprechen. ""
Stellen Sie sich nun vor, Ihr Code hat keine Funktionen / Methoden. Es ist nur eine riesige Code-Ausbreitung in Form eines Diagramms.
Sie möchten diese Ausbreitung in Methoden aufteilen. Beachten Sie, dass in jeder Methode eine bestimmte Anzahl von Blöcken vorhanden ist. Für alle anderen Methoden ist nur ein Block jeder Methode sichtbar: der erste Block (wir gehen davon aus, dass Sie nur an einem Punkt in eine Methode springen können: im ersten Block). Alle anderen Blöcke in jeder Methode sind Informationen, die in dieser Methode verborgen sind, aber jeder Block in einer Methode kann möglicherweise zu einem anderen Block in dieser Methode springen.
Um zu bestimmen, wie groß Ihre Methoden in Bezug auf die Anzahl der Blöcke pro Methode sein sollten, stellen Sie sich möglicherweise die Frage: Wie viele Methoden sollte ich haben, um die maximale potenzielle Anzahl von Abhängigkeiten (MPE) zwischen allen Blöcken zu minimieren?
Diese Antwort wird durch eine Gleichung gegeben. Wenn r die Anzahl der Methoden ist, die die MPE des Systems minimieren, und n die Anzahl der Blöcke im System ist, lautet die Gleichung: r = sqrt (n)
Und es kann gezeigt werden, dass dies die Anzahl der Blöcke pro Methode ergibt, auch sqrt (n).
quelle
Denken Sie daran, dass Sie am Ende nur aus Gründen des Re-Factorings ein Re-Factoring durchführen können, wodurch der Code möglicherweise unleserlicher wird als ursprünglich.
Ein ehemaliger Kollege von mir hatte eine bizarre Regel, dass eine Funktion / Methode nur 4 Codezeilen enthalten darf! Er versuchte, sich so streng daran zu halten, dass seine Methodennamen sich oft wiederholten und bedeutungslos wurden und die Aufrufe tief verschachtelt und verwirrend wurden.
Mein eigenes Mantra ist also geworden: Wenn Sie sich keinen anständigen Funktions- / Methodennamen für den Code vorstellen können, den Sie neu faktorisieren, stören Sie sich nicht.
quelle
Der Hauptgrund, warum ich eine Funktion normalerweise aufteile, ist entweder, dass Teile davon auch Bestandteile einer anderen Funktion in der Nähe sind, die ich schreibe, sodass die gemeinsamen Teile herausgerechnet werden. Wenn viele Felder oder Eigenschaften aus einer anderen Klasse verwendet werden, besteht eine gute Chance, dass der relevante Block im Großhandel herausgehoben und wenn möglich in die andere Klasse verschoben werden kann.
Wenn Sie einen Codeblock mit einem Kommentar oben haben, ziehen Sie in Betracht, ihn in eine Funktion zu ziehen, wobei die Funktions- und Argumentnamen den Zweck veranschaulichen, und den Kommentar für die Begründung des Codes zu reservieren.
Sind Sie sicher, dass es dort keine Teile gibt, die anderswo nützlich wären? Was ist das für eine Funktion?
quelle
Meiner Meinung nach lautet die Antwort: Wenn es zu viele Dinge tut. Ihre Funktion sollte nur die Aktionen ausführen, die Sie vom Namen der Funktion selbst erwarten. Eine andere zu berücksichtigende Sache ist, wenn Sie einige Teile Ihrer Funktionen in anderen wiederverwenden möchten; In diesem Fall kann es nützlich sein, es zu teilen.
quelle
Normalerweise unterbreche ich Funktionen, indem ich Kommentare platzieren muss, die den nächsten Codeblock beschreiben. Was zuvor in den Kommentaren stand, geht jetzt in den neuen Funktionsnamen. Dies ist keine harte Regel, aber (für mich) eine nette Faustregel. Ich mag es, wenn Code für sich selbst spricht, besser als einer, der Kommentare benötigt (da ich gelernt habe, dass Kommentare normalerweise lügen)
quelle
Dies ist teilweise Geschmackssache, aber wie ich dies feststelle, versuche ich, meine Funktionen ungefähr so lange beizubehalten, wie sie gleichzeitig (maximal) auf meinen Bildschirm passen. Der Grund dafür ist, dass es einfacher ist zu verstehen, was passiert, wenn Sie das Ganze auf einmal sehen können.
Wenn ich codiere, ist es eine Mischung aus dem Schreiben langer Funktionen und dem Umgestalten, um Bits herauszuholen, die von anderen Funktionen wiederverwendet werden könnten, und dem Schreiben kleiner Funktionen, die diskrete Aufgaben ausführen.
Ich weiß nicht, ob es eine richtige oder falsche Antwort darauf gibt (z. B. können Sie sich auf 67 Zeilen als Ihr Maximum festlegen, aber es kann Zeiten geben, in denen es sinnvoll ist, ein paar weitere hinzuzufügen).
quelle
Zu diesem Thema wurden einige gründliche Nachforschungen angestellt. Wenn Sie die wenigsten Fehler wünschen, sollte Ihr Code nicht zu lang sein. Es sollte aber auch nicht zu kurz sein.
Ich bin nicht der Meinung, dass eine Methode auf Ihre Anzeige in einer passen sollte, aber wenn Sie um mehr als eine Seite nach unten scrollen, ist die Methode zu lang.
Weitere Informationen finden Sie unter Die optimale Klassengröße für objektorientierte Software .
quelle
Ich habe bereits 500 Zeilenfunktionen geschrieben, dies waren jedoch nur große switch-Anweisungen zum Dekodieren und Beantworten von Nachrichten. Als der Code für eine einzelne Nachricht komplexer wurde als ein einzelnes Wenn-Dann-Sonst, habe ich ihn extrahiert.
Obwohl die Funktion 500 Zeilen betrug, betrugen die unabhängig gepflegten Regionen im Durchschnitt 5 Zeilen.
quelle
Normalerweise verwende ich einen testgetriebenen Ansatz zum Schreiben von Code. Bei diesem Ansatz hängt die Funktionsgröße häufig von der Granularität Ihrer Tests ab.
Wenn Ihr Test ausreichend fokussiert ist, werden Sie aufgefordert, eine kleine fokussierte Funktion zu schreiben, um den Test zu bestehen.
Dies funktioniert auch in die andere Richtung. Funktionen müssen klein genug sein, um effektiv testen zu können. Wenn ich mit Legacy-Code arbeite, stelle ich häufig fest, dass ich größere Funktionen aufspalte, um die verschiedenen Teile davon zu testen.
Normalerweise frage ich mich, "was die Verantwortung für diese Funktion ist", und wenn ich die Verantwortung nicht in einem klaren, präzisen Satz angeben kann und dies dann in einen kleinen, fokussierten Test übersetze, frage ich mich, ob die Funktion zu groß ist.
quelle
Wenn es mehr als drei Verzweigungen hat, bedeutet dies im Allgemeinen, dass eine Funktion oder Methode getrennt werden sollte, um die Verzweigungslogik in verschiedene Methoden zu kapseln.
Jede for-Schleife, if-Anweisung usw. wird dann nicht als Zweig in der aufrufenden Methode angesehen.
Cobertura für Java-Code (und ich bin sicher, es gibt andere Tools für andere Sprachen) berechnet die Anzahl der if usw. in einer Funktion für jede Funktion und summiert sie für die "durchschnittliche zyklomatische Komplexität".
Wenn eine Funktion / Methode nur drei Zweige hat, erhält sie eine Drei für diese Metrik, was sehr gut ist.
Manchmal ist es schwierig, diese Richtlinie zu befolgen, und zwar zur Validierung von Benutzereingaben. Das Einfügen von Verzweigungen in verschiedene Methoden hilft jedoch nicht nur bei der Entwicklung und Wartung, sondern auch beim Testen, da die Eingaben in die Methoden, die die Verzweigung durchführen, leicht analysiert werden können, um festzustellen, welche Eingaben zu den Testfällen hinzugefügt werden müssen, um die Verzweigungen abzudecken, die wurden nicht abgedeckt.
Wenn sich alle Zweige innerhalb einer einzigen Methode befinden würden, müssten die Eingaben seit dem Start der Methode verfolgt werden, was die Testbarkeit behindert.
quelle
Ich vermute, Sie werden viele Antworten darauf finden.
Ich würde es wahrscheinlich basierend auf den logischen Aufgaben aufteilen, die innerhalb der Funktion ausgeführt wurden. Wenn es für Sie so aussieht, als würde Ihre Kurzgeschichte zu einem Roman, würde ich vorschlagen, bestimmte Schritte zu finden und zu extrahieren.
Wenn Sie beispielsweise eine Funktion haben, die eine Zeichenfolgeneingabe verarbeitet und ein Zeichenfolgenergebnis zurückgibt, können Sie die Funktion basierend auf der Logik zum Aufteilen Ihrer Zeichenfolge in Teile, der Logik zum Hinzufügen zusätzlicher Zeichen und der Logik zum Einfügen aufteilen alles wieder zusammen als formatiertes Ergebnis.
Kurz gesagt, was auch immer Ihren Code sauber und leicht lesbar macht (sei es, indem Sie einfach sicherstellen, dass Ihre Funktion gut kommentiert oder aufgebrochen ist), ist der beste Ansatz.
quelle
Unter der Annahme, dass Sie eine Sache tun, hängt die Länge ab von:
60 Zeilen sind möglicherweise zu lang oder genau richtig. Ich vermute, dass es zu lang sein kann.
quelle
Eine Sache (und diese Sache sollte aus dem Funktionsnamen ersichtlich sein), aber nicht mehr als ein Bildschirm voller Code, unabhängig davon. Sie können auch Ihre Schriftgröße erhöhen. Und wenn Sie Zweifel haben, überarbeiten Sie es in zwei oder mehr Funktionen.
quelle
Wenn Sie vor einiger Zeit den Geist eines Tweets von Onkel Bob erweitern, wissen Sie, dass eine Funktion zu lang wird, wenn Sie das Bedürfnis haben, eine Leerzeile zwischen zwei Codezeilen zu setzen. Die Idee ist, dass, wenn Sie eine leere Zeile benötigen, um den Code zu trennen, seine Verantwortung und sein Umfang an diesem Punkt getrennt werden.
quelle
Meine Idee ist, dass wenn ich mich fragen muss, ob es zu lang ist, es wahrscheinlich zu lang ist. In diesem Bereich können kleinere Funktionen erstellt werden, da dies später im Lebenszyklus der Anwendung hilfreich sein kann.
quelle