2016-05-04 24 views
0

Wenn man die Kodierungspraxis der Zerlegung von Funktionen in kleinere Funktionen verwendet, die jeweils "eine logische Sache" machen, scheint es, als ob der Ansatz manchmal zu Code führt, der schwer zu debuggen ist.Bei einer Funktion mit mehreren logischen Schritten, bei der jeder Schritt auf N geteilte Objekte verweist, wie kann diese Funktion optimal dekonstruiert werden?

Als Beispiel stelle ich mir den folgenden Code haben:

Function Foo(Class1* p1, Class2* p2, Class3* p3) 
{ 
    Function InitializeStuff(Class1* p1, Class2* p2, Class3* p3) 
    Function DoFirstBlockOfWork(Class1* p1, Class2* p2, Class3* p3) 
    Function DoSecondBlockOfWork(Class1* p1, Class2* p2, Class3* p3) 
    Function FinishStuff(Class1* p1, Class2* p2, Class3* p3) 

} 

In diesem Beispiel gibt es drei Objektreferenzen in jedem logischen Block benötigt. Wenn nun der Debugger in einem dieser Blöcke stoppt, ist es schwierig zu wissen, woher die Eingaben in der Codedatei stammen (besonders wenn sie nicht mit der Codebasis vertraut sind).

In diesem Fall gibt es den zusätzlichen Vorteil des Verständnisses beim Lesen von oben nach unten, aber beim Debugging von unten nach oben, scheint diese Methode tatsächlich mühsamer als das Platzieren aller Funktionalität in Foo.

Also, im Falle einer Funktion mit stark gekoppelten logischen Blöcken, was ist das Argument für die Aufteilung der Funktionalität in kleinere Funktionen?

. (Anmerkung: Ich habe mit der Annahme leben, dass es immer besten Funktionen dekonstruieren zum kleinsten logischen Block)

+0

Sie erstellen eine Funktion, wenn Sie das gleiche mehrmals verwenden möchten. Angenommen, Sie definieren ein Polynom f (x) = x^2 + 3x +4 und Sie benötigen den Wert des Polynoms für x = 3, 4 und 5. In diesem Fall schreiben Sie das Polynom, wie es geschrieben wird und Sie rufen einfach f (3), f (4) und f (5) auf. Aber wenn Sie etwas nur einmal brauchen, sagen wir, wir brauchen das Polynom nur für einen einzelnen Wert 7, dann können wir einfach schreiben 7 * 7 + 3 * 7 + 4inline und wir sind fertig. – dimm

+0

Ich verstehe, dass eine Funktion in Fällen nützlich ist, in denen Code wiederverwendet wird. Die Frage, die ich stelle, ist, unabhängig von der Wiederverwendung, warum ist das Aufspalten von Funktionen auf den kleinsten logischen Block eine nützliche und gut akzeptierte Praxis, wenn es beim Debuggen unnötige Komplexität verursacht? – Veita

Antwort

1

Ablesbarkeit zählt. Gemäß dem Kommentar von dimm ist ein Anwendungsfall von Funktionen (oder Routinen oder Prozeduren usw.) wiederverwendbar.

Also, wenn Sie den gleichen Code an mehreren Stellen wiederholt haben, DRY sagt Ihnen, eine Funktion einzuführen.

Darüber hinaus sollte eine Funktion (1) lesbar und (2) leicht zu debuggen sein (und testen, wenn Sie Komponententests durchführen).

Dies führt zu der Faustregel, Funktionen zu haben, die eine Sache tun. Da sie einfacher zu benennen sind, ist das Verhalten einfacher zu definieren und zu debuggen, und wenn Sie kürzer sind, können Sie einfach auf den Code schauen und die Bedeutung identifizieren.

Wenn Sie jedoch mit diesem Ansatz zu weit gehen, kommen Sie zu dem Punkt, viele wirklich kleine Funktionen zu haben, und Sie riskieren, das große Ganze zu verlieren.

Meine Faustregel lautet: Wenn die Funktion nicht auf eine "Seite" passt (etwa 20-30 Zeilen), ist es ein Kandidat für das Teilen. Einzige Ausnahme von dieser Regel sind "listenähnliche" Methoden, die große switch/case-Anweisungen enthalten.

Einige bevorzugen eine große Funktion mit Kommentaren darin, aber ich mag mehr selbstkommendierenden Code mit beschreibenden Funktionsnamen.

Einige andere mögen kleinere Funktionen, mit nicht mehr als 8 Zeilen Code, aber diese Länge ist für meinen Geschmack sehr kurz.

YMMV.