Verschachtelte Funktionen aus rein ästhetischen Gründen erstellen?

16

Ich habe mich immer gefragt, was andere Programmierer von der Idee halten, reine ästhetische Funktionen zu kreieren.

Sagen , dass ich eine Funktion haben , die einen Teil der Daten verarbeitet: Function ProcessBigData. Sagen , dass ich mehrere Prozessschritte benötigen, nur gültig für diese Daten: Step1, Step2, Step3.

Der normale Ansatz, den ich am häufigsten im Quellcode sehe, ist das Schreiben von Kommentaren wie folgt:

Function ProcessBigData:
    # Does Step1
    Step1..
    Step1..

    #Does Step2
    Step2..
    Step2..

Was ich normalerweise mache, aber mich aufgrund des Fehlens eines solchen Codierungsstils von Kollegen immer falsch fühlte, ist:

Function ProcessBigData:
    Function Step1:
        Step1..
        Step1..

    Function Step2:
        Step2..
        Step2..

    Step1() -> Step2()

Ich bin hauptsächlich besorgt, ob es irgendwelche Nachteile für einen solchen Stil in Javascript und Python gibt

Gibt es Alternativen, die ich nicht sehe?

Slytael
quelle
3
Ich kann nichts über Python sagen, aber für Javascript gibt es Performance-Kosten für verschachtelte Funktionen: Die meisten JavaScript-Engines verwenden eine Struktur ähnlich einer verknüpften Liste, um den Gültigkeitsbereich von Variablen darzustellen. Das Hinzufügen einer zusätzlichen Funktionsebene zwingt die Engine daher möglicherweise dazu, beim Auflösen von Variablen eine längere / größere Datenstruktur zu durchsuchen. Andererseits ist die Wurzel allen Übels natürlich die vorzeitige Optimierung. :)
Marco

Antworten:

4

Es ist nicht so seltsam, wie Sie vielleicht denken. In Standard ML ist es beispielsweise üblich, den Umfang der Hilfsfunktionen einzuschränken. Zugegeben, SML hat die folgende Syntax, um dies zu vereinfachen:

local
    fun recursion_helper (iteration_variable, accumulator) =
        ... (* implementation goes here *)
in
    fun recursive_function (arg) = recursion_helper(arg, 0);
end

Ich würde diesen guten Stil in Betracht ziehen, da 1) kleine Funktionen das Denken über das Programm erleichtern und 2) dem Leser signalisieren, dass diese Funktionen nicht außerhalb dieses Bereichs verwendet werden.

Ich nehme an, es ist möglich, dass das Erstellen der inneren Funktionen bei jedem Aufruf der äußeren Funktion einen gewissen Aufwand verursacht (ich weiß nicht, ob JS oder Python das wegoptimieren), aber Sie wissen, was sie über vorzeitige Optimierung sagen.

Doval
quelle
11

Es ist normalerweise eine gute Sache, dies zu tun, wann immer es möglich ist, aber ich stelle mir diese Art von Arbeit nicht als "Schritte" vor, sondern als Unteraufgaben .

Eine Unteraufgabe ist eine bestimmte Arbeitseinheit, die ausgeführt werden kann: Sie hat eine bestimmte Verantwortung und definiert Eingaben und Ausgaben (denken Sie an das "S" in SOLID ). Eine Unteraufgabe muss nicht wiederverwendbar sein: Manche Leute denken, "Ich werde das nie von irgendetwas anderem aufrufen müssen, also warum sollte ich es als Funktion schreiben?" aber das ist ein Trugschluss.

Ich werde versuchen, auch die Vorteile und die Anwendung auf verschachtelte Funktionen (Closures) im Vergleich zu einer anderen Funktion in der Klasse zu skizzieren. Im Allgemeinen würde ich empfehlen, keine Verschlüsse zu verwenden, es sei denn, Sie benötigen speziell einen (es gibt viele Verwendungszwecke, aber das Aufteilen von Code in logische Blöcke ist keiner davon).

Lesbarkeit.

Über 200 Zeilen prozeduraler Code (Hauptteil einer Funktion) sind schwer zu lesen. 2-20 Zeilenfunktionen sind gut lesbar. Code ist für Menschen.

Geschachtelt oder nicht, Sie profitieren meistens von der Lesbarkeit, es sei denn, Sie verwenden viele Variablen aus dem übergeordneten Bereich. In diesem Fall kann es genauso schwierig sein, sie zu lesen.

Variablenbereich einschränken

Wenn Sie eine andere Funktion haben, müssen Sie den Gültigkeitsbereich der Variablen einschränken und genau das übergeben, was Sie benötigen.

Dies verbessert häufig auch die Struktur des Codes, denn wenn Sie eine Art Statusvariable aus einem früheren "Schritt" benötigen, finden Sie möglicherweise tatsächlich eine weitere Teilaufgabe, die zuerst geschrieben und ausgeführt werden muss, um diesen Wert zu erhalten. Mit anderen Worten, es ist schwieriger, stark gekoppelte Codestücke zu schreiben.

Mit verschachtelten Funktionen können Sie innerhalb der verschachtelten Funktion (Closure) auf Variablen im übergeordneten Bereich zugreifen. Dies kann sehr nützlich sein, kann aber auch zu subtilen, schwer zu findenden Fehlern führen, da die Ausführung der Funktion möglicherweise nicht so erfolgt, wie sie geschrieben wurde. Dies gilt umso mehr, wenn Sie Variablen im übergeordneten Bereich ändern (im Allgemeinen eine sehr schlechte Idee).

Unit-Tests

Jede Unteraufgabe, in der eine Funktion (oder sogar eine Klasse) implementiert ist, ist ein eigenständiger, testbarer Code. Die Vorteile von Unit Testing und TDD sind an anderer Stelle gut dokumentiert.

Die Verwendung verschachtelter Funktionen / Closures ermöglicht keine Komponententests. Für mich ist dies ein Deal-Breaker und der Grund, warum Sie nur eine andere Funktion ausüben sollten, es sei denn, es besteht ein spezifischer Bedarf für einen Abschluss.

Teamarbeit / Top-down-Design

Unteraufgaben können bei Bedarf von verschiedenen Personen unabhängig voneinander geschrieben werden.

Selbst wenn Sie selbst Code schreiben, kann es hilfreich sein, einfach eine noch nicht vorhandene Teilaufgabe aufzurufen, während Sie die Hauptfunktionalität erstellen, und sich erst dann darum zu kümmern, die Teilaufgabe tatsächlich zu implementieren, wenn Sie wissen, dass sie die gewünschten Ergebnisse in einer erzielen wird sinnvoll. Dies wird auch als Top-Down-Design / Programmierung bezeichnet.

Wiederverwendung von Code

Okay, trotz allem, was ich vorher gesagt habe, gibt es manchmal tatsächlich einen Grund später, eine Teilaufgabe für etwas anderes wiederzuverwenden. Ich befürworte überhaupt nicht den "Architektur-Astronauten" -ismus, aber wenn Sie nur lose gekoppelten Code schreiben, profitieren Sie möglicherweise später von der Wiederverwendung.

Oft bedeutet diese Wiederverwendung ein gewisses Refactoring, was durchaus zu erwarten ist. Das Refactoring der Eingabeparameter zu einer kleinen Standalone-Funktion ist jedoch VIEL einfacher als das Extrahieren aus einer mehr als 200 Zeilen umfassenden Funktion Monate nach dem Schreiben, worum es hier wirklich geht.

Wenn Sie eine verschachtelte Funktion verwenden, müssen Sie sie in der Regel ohnehin zu einer separaten Funktion umgestalten. Aus diesem Grund würde ich argumentieren, dass verschachtelt nicht der richtige Weg ist.

gregmac
quelle
2
Dies sind einige wirklich gültige Punkte für die Verwendung von Funktionen im Allgemeinen, aber ich habe Ihre Antwort nicht erhalten, wenn Sie der Meinung sind, dass NESTED-Funktionen eine gute Idee sind. Oder bekommen Sie die Funktionen im Upstream-Bereich raus?
Slytael
Entschuldigung, guter Punkt, ich habe mich mit den anderen Vorteilen beschäftigt, die ich vergessen habe, diesen Teil anzusprechen. :) Bearbeitet.
Gregmac