Ich schreibe ein kleines akademisches Forschungsprojekt über extrem lange Funktionen. Offensichtlich bin ich nicht auf der Suche nach Beispiele für schlechte Programmierung , aber für Beispiele von 100, 200 und 600 Zeilen langen Funktionen, die sinnvoll sind.
Ich werde die Linux-Kernel-Quelle mit einem Skript untersuchen, das für einen Master geschrieben wurde Grad geschrieben in der Hebräischen Universität , der verschiedene Parameter wie Anzahl der Codezeilen, Funktionskomplexität (gemessen von MCC) und andere Leckereien. Übrigens, es ist eine ordentliche Studie über Code-Analyse und ein empfohlenes Lesematerial.
Ich bin interessiert, wenn Sie einen guten Grund dafür finden können, warum eine Funktion außergewöhnlich lang sein sollte? Ich werde mich mit C beschäftigen, aber Beispiele und Argumente aus jeder Sprache wären von großem Nutzen.
Ich kann dafür Flak bekommen, aber Lesbarkeit. Eine sehr serielle, aber unabhängige Ausführung, die in N Funktionsaufrufe aufgeteilt werden könnte (von Funktionen, die nirgendwo sonst verwendet werden), profitiert nicht wirklich von der Dekomposition. Es sei denn, Sie zählen das Erreichen eines beliebigen Maximums der Funktionslänge als Vorteil.
Ich würde lieber durch N Funktionsblöcke in der richtigen Größe blättern, als in der ganzen Datei zu navigieren und N Funktionen zu treffen.
Alles, was aus anderen Quellen erzeugt wird, d. h. eine endliche Zustandsmaschine von einem Parser-Generator oder ähnlichem. Wenn es nicht für den menschlichen Verzehr bestimmt ist, sind ästhetische oder Wartbarkeitsfragen irrelevant.
Funktionen können im Laufe der Zeit länger werden, besonders wenn sie von vielen Entwicklern modifiziert werden.
Fall in Punkt: Ich vor kurzem (vor 1 Jahr oder 2 Jahren) umstrukturiert einige Legacy-Bildverarbeitungscode von 2001 oder so, dass ein paar mehrere tausend Zeilen-Funktionen enthalten. Nicht ein paar mehrere tausendzeilige Dateien - ein paar mehrere tausendzeilige Funktionen.
Im Laufe der Jahre wurde ihnen so viel Funktionalität hinzugefügt, ohne dass sie sich wirklich Mühe gaben, sie richtig zu gestalten.
Lesen Sie das Kapitel in McConnells Code Complete über Subroutinen, es gibt Richtlinien und Hinweise, wann Sie Dinge in Funktionen aufteilen sollten. Wenn Sie einen Algorithmus haben, für den diese Regeln nicht gelten, kann dies ein guter Grund für eine lange Funktion sein.
Die einzigen, die ich kürzlich programmiert habe, sind, dass sie nicht viel erreichen, um sie kleiner zu machen oder den Code weniger lesbar machen. Die Vorstellung, dass eine Funktion, die über eine bestimmte Länge hinausgeht, irgendwie an sich schlecht ist, ist einfach ein blindes Dogma. Wie jedes blind angewandte Dogma entlastet den Anhänger von der Notwendigkeit, tatsächlich darüber nachzudenken, was in einem gegebenen Fall gilt ...
Aktuelle Beispiele ...
Parsing, und Validieren einer Konfigurationsdatei mit einfacher Name = Wert-Struktur in ein Array, um jeden Wert zu konvertieren, wie ich es finde, dies ist eine massive switch-Anweisung, ein Fall pro Konfigurationsoption. Warum? Ich könnte in viele Anrufe zu 5/6 Zeilen trivialen Funktionen aufgeteilt haben. Das würde meiner Klasse etwa 20 private Mitglieder hinzufügen. Keiner von ihnen wird irgendwo anders wiederverwendet. Wenn man es in kleinere Stücke einteilt, hat es einfach nicht genug Wert hinzugefügt, um es wert zu sein, also ist es seit dem Prototyp dasselbe geblieben. Wenn ich eine andere Option möchte, fügen Sie einen weiteren Fall hinzu.
Ein weiterer Fall ist der Client- und Server-Kommunikationscode in der gleichen App und der Client. Viele Anrufe zum Lesen / Schreiben von denen jeder scheitern kann, in welchem Fall ich Kaution und Rückgabe false. Diese Funktion ist also im Prinzip linear und hat nach fast jedem Anruf einen Bail-Point (falls fehlgeschlagen, zurück). Wiederum nichts zu gewinnen, indem man es verkleinert und es nicht verkleinert.
Ich sollte auch hinzufügen, dass die meisten meiner Funktionen ein paar "Bildschirmfotos" sind, und ich versuche in komplexeren Bereichen, sie "bildschirmfüllend" zu halten, einfach weil ich die ganze Funktion auf einmal betrachten kann. Es ist in Ordnung für Funktionen, die im Wesentlichen linear sind und nicht viele komplexe Schleifen oder Bedingungen haben, so dass der Fluss einfach ist. Als letzte Anmerkung ziehe ich es vor, bei der Entscheidung, welcher Code umzuformulieren ist, Kosten-Nutzen-Überlegungen anzustellen und entsprechend zu priorisieren. Hilft dabei, das ständig halbfertige Projekt zu vermeiden.
Manchmal schreibe ich eine flache Datei (für Dritte), die Header, Trailers und Detailsätze enthält, die alle verlinkt sind. Es ist einfacher, eine lange Funktion zum Zwecke der Berechnung von Zusammenfassungen zu haben, als ein Schema zu entwickeln, um Werte durch viele kleine Funktionen zu übertragen.
Ein Punkt, von dem ich denke, dass er eine Rolle spielt, ist, dass verschiedene Sprachen und Werkzeuge unterschiedliche lexikalische Scoping-Funktionen haben.
Mit Java können Sie beispielsweise Warnungen mit einer Anmerkung unterdrücken. Es kann wünschenswert sein, den Umfang der Annotation zu begrenzen und so die Funktion für diesen Zweck kurz zu halten. In einer anderen Sprache könnte es völlig willkürlich sein, diesen Abschnitt in seine eigene Funktion zu zerlegen.
Umstritten: In JavaScript tendiere ich dazu, nur Funktionen zum Zweck der Wiederverwendung von Code zu erstellen. Wenn ein Snippet nur an einer Stelle ausgeführt wird, fällt es mir schwer, die Datei (en) zu durchlaufen, die den Spaghettis der Funktionsreferenzen folgen. Ich denke, dass Schließungen längere [Eltern-] Funktionen erleichtern und verstärken. Da JS eine interpretierte Sprache ist und der eigentliche Code über die Leitung gesendet wird, ist es gut, die Länge des Codes klein zu halten - das Erstellen übereinstimmender Deklarationen und Referenzen hilft nicht (dies könnte als vorzeitige Optimierung betrachtet werden). Eine Funktion muss in JS ziemlich lange werden, bevor ich mich dazu entscheide, sie zu zerhacken, um "Funktionen kurz zu halten".
Auch in JS ist manchmal die gesamte "Klasse" technisch eine Funktion mit vielen eingeschlossenen Unterfunktionen, aber es gibt Tools, die dabei helfen können.
Andererseits haben Variablen in JS Spielraum für die Länge der Funktion und das ist ein Faktor, der die Länge einer bestimmten Funktion begrenzen kann.
Die sehr langen Funktionen, auf die ich stoße, sind nicht in C geschrieben. Sie müssen also entscheiden, ob dies für Ihre Forschung gilt oder nicht. Was ich im Sinn habe, sind einige PowerBuilder-Funktionen, die mehrere hundert Zeilen lang sind, und zwar aus folgenden Gründen:
Dies ist ein weiterer Ort, an dem offensichtliche schlechte Programmierpraktiken der Realität entsprechen. Während jeder CS-Student im ersten Jahr sagen könnte, dass diese Biester schlecht sind, würde niemand etwas dafür ausgeben, dass sie hübscher aussehen (vorausgesetzt, dass sie zumindest noch liefern).
Bei weitem am häufigsten bin ich sehe / schreibe sind lange switch-Anweisungen oder if / else semi-switch-Anweisungen für Typen, die nicht in switch-Anweisungen dieser Sprache verwendet werden können (bereits mehrfach erwähnt). Generierter Code ist ein interessanter Fall, aber ich konzentriere mich hier auf den von Menschen geschriebenen Code. Wenn ich mein aktuelles Projekt betrachte, ist die einzige wirklich lange Funktion, die oben nicht enthalten ist (296 LOC / 650 LOT), ein Cowboy-Code, den ich als eine frühe Auswertung für die Ausgabe eines Code-Generators verwende, den ich in Zukunft verwenden werde. Ich werde es definitiv refactoring, die es aus dieser Liste entfernt.
Vor vielen Jahren arbeitete ich an einer wissenschaftlichen Software, die eine lange Funktion hatte. Die Methode verwendete eine große Anzahl von lokalen Variablen und Refactoring der Methode beibehalten, was zu einem messbaren Unterschied pro Profiling. Selbst eine Verbesserung von 1% in diesem Codeabschnitt hat Stunden Rechenzeit eingespart, so dass die Funktion lange blieb. Seitdem habe ich viel gelernt, daher kann ich nicht sagen, wie ich heute mit der Situation umgehen würde.
Geschwindigkeit:
Betrachten Sie eine Schleife:
%Vor%innerhalb einer Schleife, all diese Pushs und Sprünge können ein Faktor sein.
Dies wurde weitgehend mit der Präsentation von Inline-Funktionen auf C99 und inoffiziell davor, aber ein Code, der vorher geschrieben wurde, oder aus Kompatibilitätsgründen erstellt wurde, kann aus diesem Grund lang sein.
Auch Inline hat ihre Flüsse, einige sind auf dem Link Inline-Funktionen beschrieben.
Bearbeiten:
Als Beispiel, wie ein Aufruf einer Funktion ein Programm verlangsamen kann:
%Vor%Dies erzeugt (GCC 4.2.4):
%Vor%gegen:
%Vor%oder dagegen:
%Vor%Beide produzieren (GCC 4.2.4):
%Vor%Was ist schneller.
Der XML-Parsing-Code enthält oft riesige Mengen von Escape-Zeichen in einer Setup-Funktion.
Die Funktionen, mit denen ich arbeite (nicht schreiben), werden lang, weil sie erweitert und erweitert werden und niemand die Zeit aufwendet, um die Funktionen neu zu faktorisieren. Sie fügen einfach den Funktionen Logik hinzu, ohne an das Gesamtbild zu denken.
Ich beschäftige mich mit vielen Cut-n-Paste-Entwicklungen ...
Für das Papier ist ein Aspekt, der betrachtet werden muss, ein schlechter Wartungsplan / -zyklus, etc.
Einige Ideen, die noch nicht explizit erwähnt wurden:
Tags und Links c function code-structure mcc