it-swarm.com.de

Wann ist eine Funktion zu lang?

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?

  1. Methoden sollten nur eine logische Sache machen (über Funktionalität nachdenken)
  2. Sie sollten in der Lage sein, die Methode in einem einzigen Satz zu erklären
  3. Es sollte in die Höhe Ihres Displays passen
  4. Vermeiden Sie unnötigen Overhead (Kommentare, die auf das Offensichtliche hinweisen ...)
  5. Unit-Tests sind für kleine logische Funktionen einfacher
  6. Überprüfen Sie, ob ein Teil der Funktion von anderen Klassen oder Methoden wiederverwendet werden kann
  7. Vermeiden Sie übermäßiges Koppeln zwischen Klassen
  8. Vermeiden Sie tief verschachtelte Kontrollstrukturen

Danke an alle für die Antworten, bearbeite die Liste und stimme für die richtige Antwort ab, ich werde diese auswählen;)

Ich überarbeite jetzt mit diesen Ideen :)

124
Movaxes

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 "Tun von etwas" enthält.

Das "Tun von etwas" könnte jedoch noch einige Zeilen umfassen, daher bin ich mir nicht sicher, ob einige Zeilen die richtige Metrik für die Verwendung sind :)

Bearbeiten: Dies ist eine einzelne Codezeile, die ich letzte Woche im Zusammenhang mit der Arbeit verschickt habe (um einen Punkt zu beweisen ... ich mache mir das nicht zur Gewohnheit :)) - ich möchte auf keinen Fall 50-60 dieser bösen Jungs in meiner Methode haben : D

return level4 != null ? GetResources().Where(r => (r.Level2 == (int)level2) && (r.Level3 == (int)level3) && (r.Level4 == (int)level4)).ToList() : level3 != null ? GetResources().Where(r => (r.Level2 == (int)level2) && (r.Level3 == (int)level3)).ToList() : level2 != null ? GetResources().Where(r => (r.Level2 == (int)level2)).ToList() : GetAllResourceList();
74
Steven Robbins

Hier ist eine Liste von roten Flaggen (in keiner bestimmten Reihenfolge), die darauf hinweisen könnten, dass eine Funktion zu lang ist:

  1. tief verschachtelte Kontrollstrukturen: z.B. for-Schleifen 3 Ebenen tief oder sogar nur 2 Ebenen tief mit verschachtelten if-Anweisungen, die komplexe Bedingungen haben.

  2. Zu viele zustandsdefinierende Parameter: Durch zustandsdefinierende Parameter meine ich einen Funktionsparameter, der einen bestimmten Ausführungspfad durch die Funktion garantiert. Wenn Sie zu viele dieser Arten von Parametern abrufen, kommt es zu einer kombinatorischen Explosion von Ausführungspfaden (dies geschieht normalerweise zusammen mit Nummer 1).

  3. Logik, die in anderen Methoden dupliziert wird: Eine schlechte Wiederverwendung von Code trägt erheblich zum monolithischen prozeduralen Code bei. Viele solcher logischen Vervielfältigungen können sehr subtil sein, aber wenn sie einmal überarbeitet wurden, kann das Endergebnis ein weitaus eleganteres Design sein.

  4. Übermäßige Kopplung zwischen Klassen: Dieses Fehlen einer ordnungsgemäßen Kapselung führt dazu, dass Funktionen sich mit den intimen Eigenschaften anderer Klassen befassen und diese somit verlängern.

  5. nnö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 in verwandten Funktionen syntaktisches Rauschen erzeugen, das letztendlich zunimmt ihre Länge.

  6. Ihr riesiges Entwickler-Display ist nicht groß genug, um es anzuzeigen: Tatsächlich sind die heutigen Displays 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, dass etwas nicht stimmt.

  7. Sie können den Zweck der Funktion nicht sofort bestimmen: Wenn Sie den Zweck der Funktion nicht sofort bestimmen , können Sie ihn auch festlegen Fassen Sie diesen Zweck in einem einzigen Satz zusammen oder haben Sie enorme Kopfschmerzen, dies sollte 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, der eine absolute Freude am Lesen macht, wird seine Eleganz sofort deutlich. Und raten Sie mal: Die Funktionen sind oft sehr kurz.

201
Ryan Delucchi

Ich denke, es gibt eine große Einschränkung für das Mantra "nur eine Sache tun" auf dieser Seite. Manchmal jongliert eines viele Variablen. Teilen Sie eine lange Funktion nicht in mehrere kleinere Funktionen auf, wenn die kleineren Funktionen lange Parameterlisten haben. Dadurch wird aus einer einzelnen Funktion eine Menge hochgradig gekoppelter Funktionen ohne echten individuellen Wert.

28
jmucchiello

Eine Funktion sollte nur eines tun. Wenn Sie in einer Funktion viele kleine Dinge tun, machen Sie jede kleine Sache zu einer Funktion und rufen Sie diese Funktionen aus der langen Funktion auf.

Was Sie wirklich nicht tun möchten, ist, alle 10 Zeilen Ihrer langen Funktion in kurze Funktionen zu kopieren und einzufügen (wie in Ihrem Beispiel vorgeschlagen).

19
Jeremy Ruten

Ich bin damit einverstanden, dass eine Funktion nur eines tun sollte, aber auf welcher Ebene ist das eine.

Wenn Ihre 60 Zeilen eine Aufgabe erfüllen (aus Ihrer Programmperspektive) 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 konkrete Teile aufteilen, die für sich allein stehen. Die zu verwendende Metrik ist Funktionalität und keine Codezeile.

Ich habe an vielen Programmen gearbeitet, bei denen die Autoren das Einzige auf ein extremes Niveau gebracht haben, und alles, was am Ende dabei herauskam, war, dass es so aussah, als hätte jemand eine Granate zu einer Funktion/Methode genommen und sie in Dutzende nicht zusammenhängende 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 die Weitergabe großer Datenmengen vermeiden.

Ich glaube, der entscheidende Punkt ist, nach der 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.

16
bruceatk

60 Zeilen sind groß, aber nicht zu lang für eine Funktion. Wenn es in einem Editor auf einen Bildschirm passt, können Sie alles auf einmal sehen. Es hängt wirklich davon ab, was die Funktionen tun.

Warum kann ich eine Funktion auflösen:

  • Es ist zu lang
  • Es macht den Code wartungsfreundlicher, indem es aufschlüsselt und aussagekräftige Namen für die neue Funktion verwendet
  • Die Funktion ist nicht zusammenhängend
  • Teile der Funktion sind an sich nützlich.
  • Wenn es schwierig ist, einen aussagekräftigen Namen für die Funktion zu finden (wahrscheinlich zu viel)
11
dajorad

Größe ca. Ihre Bildschirmgröße (also holen Sie sich einen großen Pivot Widescreen und drehen Sie ihn) ... :-)

Spaß beiseite, eine logische Sache pro Funktion.

Und das Positive ist, dass Unit-Tests mit kleinen logischen Funktionen, die eine Funktion ausführen, sehr viel einfacher sind. Große Funktionen, die vieles leisten, sind schwerer zu überprüfen!

/ Johan

6
Johan

Faustregel: Wenn eine Funktion Codeblöcke enthält, die etwas bewirken, das etwas vom Rest des Codes abweicht, setzen Sie sie in eine separate Funktion. Beispiel:

function build_address_list_for_Zip($Zip) {

    $query = "SELECT * FROM ADDRESS WHERE Zip = $Zip";
    $results = perform_query($query);
    $addresses = array();
    while ($address = fetch_query_result($results)) {
        $addresses[] = $address;
    }

    // now create a Nice looking list of
    // addresses for the user
    return $html_content;
}

viel schöner:

function fetch_addresses_for_Zip($Zip) {
    $query = "SELECT * FROM ADDRESS WHERE Zip = $Zip";
    $results = perform_query($query);
    $addresses = array();
    while ($address = fetch_query_result($results)) {
        $addresses[] = $address;
    }
    return $addresses;
}

function build_address_list_for_Zip($Zip) {

    $addresses = fetch_addresses_for_Zip($Zip);

    // now create a Nice looking list of
    // addresses for the user
    return $html_content;
}

Dieser Ansatz hat zwei Vorteile:

  1. Wenn Sie Adressen für eine bestimmte Postleitzahl abrufen müssen, können Sie die sofort verfügbare Funktion verwenden.

  2. Wenn Sie die Funktion build_address_list_for_Zip() jemals wieder lesen müssen, wissen Sie, was der erste Codeblock tun wird (er ruft Adressen für eine bestimmte Postleitzahl ab, zumindest das, was Sie aus dem Funktionsnamen ableiten können). Wenn Sie den Abfragecode inline gelassen hätten, müssten Sie diesen Code zuerst 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 klein wie möglich zu halten Dies ist möglich, da Funktionsaufrufe in PHP sehr, sehr teuer sind. Ich weiß es nicht, da ich nie Benchmarks durchgeführt habe. Wenn dies der Fall ist, ist es wahrscheinlich besser, wenn Sie keine der Antworten auf Ihre Frage befolgen, wenn es sich um eine Anwendung handelt sehr "leistungsempfindlich" ;-)]

6
EricSchaefer

Werfen Sie einen Blick auf McCabes Zyklomatik, in der er seinen Code in ein Diagramm aufteilt: "Jeder Knoten im Diagramm entspricht einem Codeblock im Programm, in dem der Fluss sequentiell ist und die Bögen den Verzweigungen im Programm entsprechen. "

Stellen Sie sich vor, Ihr Code hat keine Funktionen/Methoden. Es ist nur eine riesige Menge Code in Form eines Graphen.

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 an nur einem Punkt in eine Methode springen können: dem 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 muss ich verwenden, um die maximale Anzahl möglicher 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 die Anzahl der Blöcke pro Methode auch sqrt (n) ist.

6
Ed Kirwan

Meine persönliche Heuristik ist, dass es zu lang ist, wenn ich das Ganze nicht ohne Scrollen sehen kann.

6
Ferruccio

Denken Sie daran, dass Sie das Re-Factoring möglicherweise nur um des Re-Factorings willen durchführen und den Code möglicherweise unleserlicher machen können, als dies ursprünglich der Fall war.

Ein ehemaliger Kollege von mir hatte eine bizarre Regel, dass eine Funktion/Methode nur 4 Codezeilen enthalten darf! Er versuchte, sich so strikt daran zu halten, dass sich seine Methodennamen oft wiederholten und bedeutungslos wurden und die Aufrufe zutiefst verschachtelt und verwirrend wurden.

So ist mein eigenes Mantra geworden: Wenn Sie sich keinen anständigen Funktions-/Methodennamen für das Stück Code vorstellen können, das Sie neu faktorisieren, machen Sie sich keine Sorgen.

4
Saltmeister

Der Hauptgrund, warum ich eine Funktion in der Regel aufspalte, liegt entweder darin, dass Teile davon auch Bestandteile einer anderen in der Nähe befindlichen Funktion 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 betreffende Block im Großhandel herausgenommen und wenn möglich in die andere Klasse verschoben werden kann.

Wenn Sie einen Codeblock mit einem Kommentar oben haben, ziehen Sie es 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 keine Teile gibt, die an anderer Stelle nützlich wären? Was ist das für eine Funktion?

2
Jeffrey Hantin

Meiner Meinung nach lautet die Antwort: Wenn es zu viel macht. 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.

2
Pierpaolo

Normalerweise unterbreche ich Funktionen, indem ich Kommentare platzieren muss, die den nächsten Codeblock beschreiben. Was zuvor in den Kommentaren stand, steht jetzt unter dem neuen Funktionsnamen. Dies ist keine harte Regel, aber (für mich) eine nette Faustregel. Ich mag Code, der für sich selbst spricht, besser als ein Code, der Kommentare benötigt (da ich gelernt habe, dass Kommentare normalerweise lügen)

2
Olaf Kock

Normalerweise benutze 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 also mit altem Code arbeite, stelle ich häufig fest, dass ich größere Funktionen zerlege, um die verschiedenen Teile davon zu testen.

Normalerweise frage ich mich, "was ist die Verantwortung dieser Funktion", und wenn ich die Verantwortung nicht in einem klaren, präzisen Satz wiedergeben kann und dies dann in einen kleinen, fokussierten Test übersetze, frage ich mich, ob die Funktion zu groß ist.

2
lexicalscope

Dies ist zum Teil Geschmackssache, aber wie ich das feststelle, versuche ich, meine Funktionen ungefähr nur so lange beizubehalten, wie sie auf einmal (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 programmiere, ist es eine Mischung aus dem Schreiben langer Funktionen und dem Refactoring, um Bits herauszuholen, die von anderen Funktionen wiederverwendet werden könnten.

Ich weiß nicht, ob es eine richtige oder eine falsche Antwort darauf gibt (z. B. können Sie sich auf maximal 67 Zeilen festlegen, aber manchmal ist es sinnvoll, ein paar weitere hinzuzufügen).

1
Andrew Hedges

Genau zu diesem Thema wurde gründlich recherchiert. Wenn Sie die wenigsten Fehler haben möchten, sollte Ihr Code nicht zu lang sein. Es sollte aber auch nicht zu kurz sein.

Ich bin nicht einverstanden, dass eine Methode in eine Anzeige passt, aber wenn Sie 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 .

1
Ian Hickman

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 für ein einzelnes Wenn-Dann-Sonst, habe ich ihn extrahiert.

Obwohl die Funktion 500 Zeilen umfasste, bildeten die unabhängig verwalteten Bereiche im Wesentlichen einen Durchschnitt von 5 Zeilen.

1
Joshua

Wenn es mehr als drei Verzweigungen hat, bedeutet dies im Allgemeinen, dass eine Funktion oder Methode getrennt werden sollte, um die Verzweigungslogik in verschiedenen Methoden zu kapseln.

Jede for-Schleife, if-Anweisung usw. wird dann in der aufrufenden Methode nicht als Verzweigung angesehen.

Cobertura for Java code (und ich bin sicher, es gibt andere Tools für andere Sprachen) berechnet die Anzahl von 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 zu den 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 die Verzweigungen abdecken wurden nicht abgedeckt.

Wenn sich alle Zweige innerhalb einer einzelnen Methode befänden, müssten die Eingaben seit dem Start der Methode nachverfolgt werden, was die Testbarkeit beeinträchtigt.

0
MetroidFan2002

Ich vermute, Sie werden viele Antworten darauf finden.

Ich würde es wahrscheinlich auf der Grundlage der logischen Aufgaben aufteilen, die innerhalb der Funktion ausgeführt wurden. Wenn es für Sie so aussieht, als würde sich Ihre Kurzgeschichte in einen Roman verwandeln, würde ich vorschlagen, bestimmte Schritte zu finden und zu extrahieren.

Wenn Sie beispielsweise eine Funktion haben, die Zeichenfolgeneingaben verarbeitet und ein Zeichenfolgenergebnis zurückgibt, können Sie die Funktion auf der Grundlage der Logik zum Aufteilen der Zeichenfolge in Teile, der Logik zum Hinzufügen zusätzlicher Zeichen und der Logik zum Platzieren der Zeichenfolge aufteilen Alles wieder zusammen als formatiertes Ergebnis.

Kurz gesagt, was auch immer Ihren Code sauber und einfach lesbar macht (sei es nur, indem Sie sicherstellen, dass Ihre Funktion gut kommentiert oder aufgeschlüsselt ist), ist der beste Ansatz.

0
Phil.Wheeler

unter der Annahme, dass Sie eins tun, hängt die Länge ab von:

  • was machst du
  • welche sprache benutzt du
  • mit wie vielen Abstraktionsebenen müssen Sie im Code umgehen?

60 Zeilen sind möglicherweise zu lang oder genau richtig. Ich vermute, dass es zu lange sein kann.

0
Ray Tayek

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 Schrift vergrößern. Im Zweifelsfall können Sie es in zwei oder mehr Funktionen umgestalten.

0
gkrogers

Meine Idee ist, wenn ich mich fragen muss, ob es zu lang ist, ist es wahrscheinlich zu lang. In diesem Bereich können kleinere Funktionen erstellt werden, da dies später im Lebenszyklus der Anwendung hilfreich sein kann.

0
sokket

Wenn Sie den Geist eines Tweets von Onkel Bob vor einiger Zeit erweitern, wissen Sie, dass eine Funktion zu lang wird, wenn Sie das Gefühl haben, eine Leerzeile zwischen zwei Codezeilen zu setzen. Die Idee ist, dass, wenn Sie eine leere Zeile zum Trennen des Codes benötigen, die Verantwortung und der Geltungsbereich an diesem Punkt getrennt werden.

0
Mark Bostleman