Sollten wir lokale Variablen entfernen, wenn wir können?


95

Um beispielsweise eine CPU in Android eingeschaltet zu lassen, kann ich folgenden Code verwenden:

PowerManager powerManager = (PowerManager)getSystemService(POWER_SERVICE);
WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "abc");
wakeLock.acquire();

Ich denke aber die lokalen Variablen powerManagerund wakeLockkönnen beseitigt werden:

((PowerManager)getSystemService(POWER_SERVICE))
    .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyWakelockTag")
    .acquire();

Eine ähnliche Szene wird in der iOS-Benachrichtigungsansicht angezeigt, z. B .: von

UIAlertView *alert = [[UIAlertView alloc]
    initWithTitle:@"my title"
    message:@"my message"
    delegate:nil
    cancelButtonTitle:@"ok"
    otherButtonTitles:nil];
[alert show];

-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
    [alertView release];
}

zu:

[[[UIAlertView alloc]
    initWithTitle:@"my title"
    message:@"my message"
    delegate:nil
    cancelButtonTitle:@"ok"
    otherButtonTitles:nil] show];

-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
    [alertView release];
}

Ist es empfehlenswert, eine lokale Variable zu entfernen, wenn sie nur einmal im Gültigkeitsbereich verwendet wird?


95
Nicht unbedingt. Manchmal macht es den Code klarer, einmalige Variablen zu verwenden, und in den meisten anständigen Programmiersprachen fallen für diese zusätzlichen Variablen nur sehr geringe (wenn überhaupt) Laufzeitkosten an.
Robert Harvey

75
Dies erschwert auch das Durchlaufen des Codes mit einem Debugger. Und Sie müssen auch sicher sein (abhängig von der Sprache), dass der erste Ausdruck weder NULL noch ein Fehler ist.
GroßmeisterB

78
Nein, das ist es nicht. In der Tat ist es empfehlenswert, lokale Variablen einzuführen , um Ketten von Methodenaufrufen zu unterbrechen. Es ist wahrscheinlich, dass der generierte Maschinencode identisch ist, und es ist fast garantiert, dass der Quellcode besser lesbar und daher besser ist.
Kilian Foth

48
Versuch das. Schließen Sie nun den Debugger an und versuchen Sie, den Status von PowerManager oder WakeLock zu ermitteln. Erkenne deinen Fehler. Früher dachte ich dasselbe ("Was ist mit all diesen Einheimischen?"), Bis ich die meiste Zeit damit verbringen musste, den Code zu untersuchen.
Faerindel

42
Selbst in Ihrem Beispiel hat Ihre "eliminierte" Version eine Bildlaufleiste und passt nicht vollständig auf meinen Bildschirm, was das Lesen erheblich erschwert.
Erik

Antworten:


235

Code wird viel öfter gelesen als geschrieben, daher sollten Sie Mitleid mit der armen Seele haben, die den Code in sechs Monaten lesen muss (Sie vielleicht) und nach dem klarsten und am einfachsten zu verstehenden Code streben wird. Meiner Meinung nach ist die erste Form mit lokalen Variablen viel verständlicher. Ich sehe drei Aktionen in drei Zeilen, anstatt drei Aktionen in einer Zeile.

Und wenn Sie glauben, Sie optimieren etwas, indem Sie lokale Variablen entfernen, tun Sie dies nicht. Ein moderner Compiler legt powerManagerin einem Register 1 fest , ob eine lokale Variable verwendet wird oder nicht, um die newWakeLockMethode aufzurufen . Gleiches gilt für wakeLock. So erhalten Sie in beiden Fällen denselben kompilierten Code.

1 Wenn Sie zwischen der Deklaration und der Verwendung der lokalen Variablen viel Code verwendet haben, wird dieser möglicherweise auf dem Stapel abgelegt, es handelt sich jedoch nur um ein geringfügiges Detail.


2
Sie können nicht wissen, ob es viel Code gibt, der Optimierer könnte einige Funktionen einbinden.
Matthieu M.

2
Nun, ich denke das Gegenteil, wenn ich sehe, dass lokale Variablen mehrfach für jede Funktion initialisiert werden, wo sie verwendet werden, anstatt Attribute zu sein, wenn es möglich ist, werde ich nach dem Grund suchen, im Grunde werde ich die Zeilen gründlich lesen und sie nur damit vergleichen Stellen Sie sicher, dass sie gleich sind. Also werde ich Zeit verschwenden.
Walfrat

15
@Walfrat Lokale Variablen werden mehrmals initialisiert? Autsch. Niemand schlug vor, Einheimische
wiederzuverwenden

32
@ Walfrat-Mitglieder verursachen Nebenwirkungen und sollten nur verwendet werden, wenn Sie den Status zwischen Aufrufen an öffentliche Mitglieder beibehalten möchten. Diese Frage scheint sich mit der Verwendung von local für die temporäre Speicherung von Zwischenberechnungen zu befassen.
Gusdor

4
F: Sollten wir lokale Variablen entfernen, wenn wir können? A: Nein.
Simon

94

Einige hoch gelobte Kommentare sagten dies aus, aber keine der Antworten, die ich gesehen habe, tat es. Deshalb werde ich es als Antwort hinzufügen. Ihr Hauptfaktor bei der Entscheidung für dieses Problem ist:

Debuggbarkeit

Oft verbringen Entwickler viel mehr Zeit und Mühe mit dem Debuggen als mit dem Schreiben von Code.

Mit lokalen Variablen können Sie:

  • Haltepunkt in der Zeile, der er zugewiesen wurde (mit allen anderen Haltepunktdekorationen, wie z. B. bedingtem Haltepunkt usw.)

  • Überprüfen / beobachten / drucken / ändern Sie den Wert der lokalen Variablen

  • Fang Probleme, die durch Abgüsse entstehen.

  • Lesbare Stapelspuren haben (Zeile XYZ hat eine Operation anstelle von 10)

Ohne lokale Variablen sind alle oben genannten Aufgaben, abhängig von Ihrem Debugger, entweder viel schwieriger, extrem schwierig oder überhaupt nicht möglich.

Befolgen Sie daher die berüchtigte Maxime (schreiben Sie den Code so, als ob der nächste Entwickler, der ihn selbst wartet, ein verrückter Wahnsinniger ist, der weiß, wo Sie leben), und gehen Sie auf die Seite der einfacheren Debug-Fähigkeit , dh verwenden Sie lokale Variablen .


20
Ich würde hinzufügen, dass Stacktraces viel weniger nützlich sind, wenn es viele Aufrufe auf einer einzelnen Leitung gibt. Wenn Sie in Zeile 50 eine Nullzeiger-Ausnahme haben und in dieser Zeile 10 Aufrufe vorhanden sind, werden die Dinge nicht stark eingeschränkt. In einer Produktionsanwendung ist dies häufig der größte Teil der Informationen, die Sie aus einem Fehlerbericht erhalten.
JimmyJames

3
Das Durchlaufen von Code ist auch viel schwieriger. Wenn es call () -> call () -> call () -> call () gibt, ist es mühsam, in den dritten Methodenaufruf einzusteigen. Viel einfacher, wenn es vier lokale Variablen gibt
gnasher729

3
@FrankPuffer - wir müssen uns mit der realen Welt auseinandersetzen. Wo Debugger nicht das tun, was Sie vorschlagen.
DVK

2
@DVK: Tatsächlich gibt es mehr Debugger, als ich dachte, mit denen Sie einen Ausdruck untersuchen oder beobachten können. MS Visual Studio (seit Version 2013) verfügt über diese Funktion für C ++ und C #. Eclipse hat es für Java. In einem anderen Kommentar erwähnte Faerindel IE11 für JS.
Frank Puffer

4
@DVK: Ja, viele Debugger aus der realen Welt tun nicht das, was ich vorschlage. Aber das hat nichts mit dem ersten Teil meines Kommentars zu tun. Ich finde es einfach verrückt anzunehmen, dass die Person, die meinen Code warten wird, hauptsächlich über einen Debugger mit ihm interagiert. Debugger eignen sich für bestimmte Zwecke hervorragend, aber wenn sie das Hauptwerkzeug für die Codeanalyse erhalten, würde ich sagen, dass etwas ernsthaft falsch ist, und ich würde versuchen, es zu beheben, anstatt den Code für das Debuggen zu verbessern.
Frank Puffer

47

Nur wenn es den Code verständlicher macht. In Ihren Beispielen ist das Lesen meiner Meinung nach schwieriger.

Das Eliminieren der Variablen im kompilierten Code ist für jeden seriösen Compiler eine triviale Operation. Sie können die Ausgabe selbst überprüfen.


1
Das hat mit dem Styling ebenso zu tun wie mit der Verwendung von Variablen. Die Frage wurde nun bearbeitet.
Jodrell

29

Ihre Frage "Ist es eine gute Praxis, eine lokale Variable zu entfernen, wenn sie nur einmal im Gültigkeitsbereich verwendet wird?" testet die falschen Kriterien. Das Dienstprogramm einer lokalen Variablen hängt nicht davon ab, wie oft sie verwendet wird, sondern davon, ob der Code dadurch klarer wird. Das Beschriften von Zwischenwerten mit aussagekräftigen Namen kann die Übersichtlichkeit in Situationen wie der von Ihnen dargestellten verbessern, da der Code in kleinere, besser verdauliche Teile zerlegt wird.

Meiner Ansicht nach ist der von Ihnen vorgelegte unveränderte Code klarer als der von Ihnen geänderte Code und sollte daher unverändert bleiben. Tatsächlich würde ich erwägen, eine lokale Variable aus Ihrem geänderten Code zu ziehen, um die Übersichtlichkeit zu verbessern.

Ich würde keine Auswirkung auf die Leistung von den lokalen Variablen erwarten, und selbst wenn es eine gibt, ist sie wahrscheinlich zu klein, um in Betracht gezogen zu werden, es sei denn, der Code befindet sich in einer sehr engen Schleife in einem geschwindigkeitskritischen Teil des Programms.


Ich denke, das hat viel mit Styling und der Verwendung von Leerzeichen zu tun. Die Frage wurde bearbeitet.
Jodrell

15

Vielleicht.

Persönlich würde ich zögern, lokale Variablen zu entfernen, wenn es sich um eine Typumwandlung handelt. Ich finde Ihre kompakte Version weniger lesbar, da sich die Anzahl der Klammern einer mentalen Grenze nähert, die ich habe. Es wird sogar ein neuer Satz von Klammern eingeführt, der in der etwas ausführlicheren Version unter Verwendung einer lokalen Variablen nicht benötigt wurde.

Die vom Code-Editor bereitgestellte Syntaxhervorhebung kann solche Bedenken in gewissem Maße abmildern.

Für mich ist dies der bessere Kompromiss:

PowerManager powerManager = (PowerManager)getSystemService(POWER_SERVICE);
powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "abc").acquire();

Auf diese Weise wird eine lokale Variable beibehalten und die andere über Bord geworfen. In C # würde ich das Schlüsselwort var in der ersten Zeile verwenden, da der Typecast bereits die Typinformationen enthält.


13

Obwohl ich die Gültigkeit der Antworten akzeptiere, die lokale Variablen bevorzugen, werde ich den Anwalt des Teufels spielen und eine konträre Sichtweise vertreten.

Ich persönlich bin etwas dagegen, lokale Variablen nur zu Dokumentationszwecken zu verwenden, obwohl dieser Grund selbst anzeigt, dass die Praxis einen Wert hat: Die lokalen Variablen dokumentieren den Code faktisch pseudodokumentiert.

In Ihrem Fall ist das Hauptproblem die Einrückung und nicht das Fehlen von Variablen. Sie könnten (und sollten) den Code wie folgt formatieren:

((PowerManager)getSystemService(POWER_SERVICE))
        .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,"MyWakelockTag")
        .acquire();

Vergleichen Sie das mit der Version mit lokalen Variablen:

PowerManager powerManager=(PowerManager)getSystemService(POWER_SERVICE);
WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,"abc");
wakeLock.acquire();

Mit den lokalen Variablen: Die Namen geben dem Leser einige Informationen und die Typen sind klar spezifiziert, aber die tatsächlichen Methodenaufrufe sind weniger sichtbar und (meiner Meinung nach) nicht so klar.

Ohne Verwendung lokaler Variablen: Es ist übersichtlicher und die Methodenaufrufe sind sichtbarer, aber Sie können weitere Informationen zu den zurückgegebenen Werten anfordern. Einige IDEs können dies jedoch anzeigen.

Drei weitere Kommentare:

  1. Meiner Meinung nach kann das Hinzufügen von Code, der keine funktionale Verwendung hat, manchmal verwirrend sein, da der Leser feststellen muss, dass er tatsächlich keine funktionale Verwendung hat und nur zur "Dokumentation" dient. Wenn diese Methode beispielsweise 100 Zeilen lang wäre, wäre es nicht offensichtlich, dass die lokalen Variablen (und damit das Ergebnis des Methodenaufrufs) zu einem späteren Zeitpunkt nicht erforderlich sind, es sei denn, Sie lesen die gesamte Methode.

  2. Das Hinzufügen der lokalen Variablen bedeutet, dass Sie deren Typen angeben müssen. Dies führt zu einer Abhängigkeit im Code, die ansonsten nicht vorhanden wäre. Wenn sich der Rückgabetyp der Methoden geringfügig ändern würde (z. B. wurde er umbenannt), müssten Sie Ihren Code aktualisieren, während Sie dies ohne die lokalen Variablen nicht tun würden.

  3. Das Debuggen mit lokalen Variablen könnte einfacher sein, wenn Ihr Debugger keine von Methoden zurückgegebenen Werte anzeigt. Aber das Update dafür ist, die Mängel in den Debuggern zu beheben, nicht den Code zu ändern.


Bezüglich Ihrer weiteren Kommentare: 1. Dies sollte kein Problem sein, wenn Sie die Konvention befolgen, dass lokale Variablen so nah wie möglich an dem Ort deklariert werden, an dem sie verwendet werden, und Ihre Methoden eine angemessene Länge haben. 2. Nicht in einer Programmiersprache mit Typinferenz. 3. Gut geschriebener Code benötigt oft überhaupt keinen Debugger.
Robert Harvey

2
Ihr erstes Codebeispiel funktioniert nur, wenn Sie Ihre Objekte mit fließenden Schnittstellen oder einem "Builder" -Muster erstellen, das ich nicht überall in meinem Code verwenden würde, sondern nur selektiv.
Robert Harvey

1
Ich denke, die Frage geht davon aus, dass die lokalen Variablen funktional redundant sind und daher für den Fall ein Schnittstellentyp erforderlich ist. Wir könnten es wahrscheinlich schaffen, Einheimische zu entfernen, wenn es anders wäre als durch verschiedene Verrenkungen, aber ich dachte daran, dass wir uns den einfachen Fall angesehen haben.
rghome

1
Ich finde deine Variante noch schlechter für die Lesbarkeit. Ich war vielleicht zu sehr mit VB.net in Berührung gekommen, aber mein erster Eindruck bei Ihrem Beispiel war: "Wo ist die WITH-Anweisung geblieben? Habe ich sie versehentlich gelöscht?" Ich muss zweimal schauen, was los ist, und das ist nicht gut, wenn ich den Code viele Monate später noch einmal überprüfen muss.
Tonny

1
@Tonny für mich ist es besser lesbar: Die Einheimischen fügen nichts hinzu, was aus dem Kontext nicht ersichtlich ist. Ich habe mein Java frontal, also keine withAussagen. Es wären normale Formatierungskonventionen in Java.
Rghome

6

Hier gibt es eine Spannung von Motivationen. Die Einführung temporärer Variablen kann das Lesen von Code erleichtern. Sie können jedoch auch andere mögliche Umgestaltungen verhindern und erschweren, z. B. Extraktionsmethode und Temp durch Abfrage ersetzen . Diese letzteren Arten von Refactorings bieten, falls angebracht, häufig viel größere Vorteile als die temporäre Variable.

Zu den Beweggründen für diese letztgenannten Umgestaltungen schreibt Fowler :

"Das Problem mit Temps ist, dass sie temporär und lokal sind. Weil sie nur im Kontext der Methode, in der sie verwendet werden, gesehen werden können, tendieren Temps dazu, längere Methoden zu fördern, da dies der einzige Weg ist, auf dem Sie das Temp erreichen können Durch Ersetzen des Temps durch eine Abfragemethode kann jede Methode in der Klasse auf die Informationen zugreifen. Dies hilft sehr dabei, einen saubereren Code für die Klasse zu finden. "

Verwenden Sie also temporäre Dateien, wenn sie den Code besser lesbar machen, insbesondere, wenn dies für Sie und Ihr Team eine lokale Norm ist. Beachten Sie jedoch, dass es dadurch manchmal schwieriger wird, größere alternative Verbesserungen zu erkennen. Wenn Sie Ihre Fähigkeit entwickeln können, zu spüren, wann es sich lohnt, auf solche Zeiträume zu verzichten und sich dabei wohler zu fühlen, dann ist das wahrscheinlich eine gute Sache.

FWIW Ich persönlich habe es zehn Jahre lang vermieden, Fowlers Buch "Refactoring" zu lesen, weil ich mir vorstellte, dass es zu solchen relativ einfachen Themen nicht viel zu sagen gibt. Ich habe mich völlig geirrt. Als ich es schließlich las, änderte es meine täglichen Codierungspraktiken zum Besseren.


1
Gute Antwort. Der beste Code, den ich geschrieben habe (und den ich von anderen gesehen habe), hatte oft viele kleine (2, 3 Zeilen) Methoden, die den Platz von solchen temporären Variablen einnehmen konnten. Im Zusammenhang steht diese umstrittene Annahme, dass private Methoden stinken .... Gut durchdacht und ich habe es oft für wahr befunden.
Stijn de Witt

Die Temps in dieser Frage beziehen sich bereits auf Funktionen, daher ist diese Antwort für die Frage von OPs irrelevant.
user949300

@ user949300 Ich denke nicht, dass es irrelevant ist. Ja, die Temps im spezifischen Beispiel des OP sind die Rückgabewerte von Funktionen oder Methoden. Aber das OP ist sehr klar, das ist nur ein Beispielszenario. Die eigentliche Frage ist die viel allgemeinere: "Sollten wir temporäre Variablen entfernen, wenn wir können?"
Jonathan Hartley

1
OK, ich bin verkauft, ** versuchte ** meine Stimme zu ändern. Aber oft, wenn ich über den begrenzten Umfang der OP-Frage hinausgehe, werde ich sauer. SO kann launisch sein ... :-). Ähm, ich kann meine Stimme nicht ändern, bis du sie bearbeitest, egal ...
user949300

@ user949300 Heilige Kuh! Eine Person, die ihre Meinung ändert, wenn sie die Probleme sorgfältig in Betracht zieht! Sie, mein Herr, sind eine Seltenheit, und ich ziehe Ihnen meine Mütze ab.
Jonathan Hartley

3

Nun, es ist eine gute Idee, lokale Variablen zu eliminieren, wenn Sie den Code dadurch besser lesbar machen können . Ihr langer Einzeiler ist nicht lesbar, aber dann folgt er einem ziemlich ausführlichen OO-Stil. Wenn du es auf so etwas wie reduzieren könntest

acquireWakeLock(POWER_SERVICE, PARTIAL, "abc");

dann würde ich sagen, dass es wahrscheinlich eine gute Idee wäre. Vielleicht können Sie Hilfsmethoden einführen, um es auf etwas Ähnliches zu reduzieren. Wenn Code wie dieser mehrmals vorkommt, lohnt es sich möglicherweise.


2

Betrachten wir hier das Gesetz von Demeter . Im Wikipedia-Artikel zu LoD heißt es:

Das Gesetz von Demeter für Funktionen verlangt, dass eine Methode m eines Objekts O nur die Methoden der folgenden Arten von Objekten aufrufen darf:

  1. O selbst
  2. Parameter von m
  3. Alle innerhalb von m erstellten / instanziierten Objekte
  4. O's direkte Komponentenobjekte
  5. Eine globale Variable, auf die O im Bereich von m zugreifen kann

Eine der Konsequenzen der Befolgung dieses Gesetzes besteht darin, dass Sie vermeiden sollten, Methoden von Objekten aufzurufen, die von anderen Methoden in einer langen, gepunkteten Zeichenfolge zurückgegeben werden, wie im obigen zweiten Beispiel gezeigt:

((PowerManager)getSystemService(POWER_SERVICE)).newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,"MyWakelockTag").acquire();

Es ist wirklich schwierig herauszufinden, was dies tut. Um es zu verstehen, müssen Sie verstehen, was jede Prozedur tut, und dann entschlüsseln, welche Funktion für welches Objekt aufgerufen wird. Der erste Codeblock

PowerManager powerManager=(PowerManager)getSystemService(POWER_SERVICE);
WakeLock wakeLock=powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,"abc");
wakeLock.acquire();

zeigt deutlich, was Sie tun möchten: Sie erhalten ein PowerManager-Objekt, erhalten ein WakeLock-Objekt vom PowerManager und erhalten dann das wakeLock. Das obige Beispiel folgt Regel 3 von LoD - Sie instanziieren diese Objekte in Ihrem Code und können sie daher verwenden, um das zu tun, was Sie benötigen.

Vielleicht sollten Sie beim Erstellen von Software auch der Übersichtlichkeit halber schreiben, nicht der Kürze halber. 90% der gesamten Softwareentwicklung ist Wartung. Schreiben Sie niemals einen Code, den Sie nicht gerne pflegen würden.

Viel Glück.


3
Mich würde interessieren, wie flüssige Syntax in diese Antwort passt. Bei korrekter Formatierung ist die flüssige Syntax gut lesbar.
Gusdor

12
Das ist nicht das, was "Gesetz von Demeter" bedeutet. Das Gesetz von Demeter ist keine Übung zum Zählen von Punkten. es bedeutet im Wesentlichen "nur mit deinen unmittelbaren Freunden sprechen".
Robert Harvey

5
Der Zugriff auf dieselben Methoden und Mitglieder über eine Zwischenvariable ist immer noch ein ebenso schwerwiegender Verstoß gegen das Gesetz von Demeter. Sie haben es nur verdeckt, nicht behoben.
Jonathan Hartley

2
Im Sinne des "Gesetzes von Demeter" sind beide Versionen gleichermaßen "schlecht".
Hulk

@Gusdor war sich einig, dass dies eher ein Kommentar zum Styling im Fragebeispiel ist. Die Frage wurde nun bearbeitet.
Jodrell

2

Zu beachten ist, dass Code häufig in verschiedenen "Tiefen" gelesen wird. Dieser Code:

PowerManager powerManager = (PowerManager)getSystemService(POWER_SERVICE);
WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "abc");
wakeLock.acquire();

ist leicht zu "überfliegen". Es sind 3 Aussagen. Zuerst kommen wir mit einem PowerManager. Dann kommen wir mit einem WakeLock. Dann haben wir acquiredie wakeLock. Ich kann das sehr leicht erkennen, wenn ich mir den Anfang jeder Zeile ansehe. Einfache Variablenzuweisungen sind zum Teil sehr leicht als "Typ varName = ..." zu erkennen und überfliegen geistig das "...". Ebenso ist die letzte Aussage offensichtlich nicht die Form der Zuweisung, sondern es handelt sich nur um zwei Namen, so dass das "Wesentliche" sofort ersichtlich ist. Oft ist das alles, was ich wissen müsste, wenn ich nur versuchen würde zu antworten: "Was macht dieser Code?" auf einem hohen Niveau.

Wenn ich einem subtilen Fehler nachjage, von dem ich denke, dass er hier ist, muss ich das natürlich viel genauer durchgehen und werde mich tatsächlich an die "..." erinnern. Die separate Anweisungsstruktur hilft mir jedoch immer noch dabei, eine Anweisung nach der anderen auszuführen (besonders nützlich, wenn ich mich eingehender mit der Implementierung der in jeder Anweisung aufgerufenen Dinge befassen muss. Wenn ich zurückkomme, habe ich "eine Einheit" vollständig verstanden. und kann dann mit der nächsten Anweisung fortfahren).

((PowerManager)getSystemService(POWER_SERVICE))
    .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyWakelockTag")
    .acquire();

Jetzt ist alles eine Aussage. Die Struktur der obersten Ebene ist nicht so einfach zu überfliegen; In der Originalversion des OP hätte ich ohne Zeilenumbrüche und Einrückungen, um eine Struktur visuell zu kommunizieren, Klammern zählen müssen, um sie in eine Folge von 3 Schritten zu dekodieren. Wenn einige der mehrteiligen Ausdrücke ineinander verschachtelt und nicht als eine Kette von Methodenaufrufen angeordnet sind, könnte dies immer noch ähnlich aussehen. Daher muss ich vorsichtig sein, wenn ich dem vertraue, ohne Klammern zu zählen. Und wenn ich der Einrückung vertraue und nur bis zum letzten Punkt überfliege, was .acquire()sagt mir das alles ?

Manchmal kann das aber auch das sein, was Sie wollen. Wenn ich Ihre Transformation auf halbem Wege anwende und schreibe:

WakeLock wakeLock =
     ((PowerManeger)getSystemService(POWER_SERVICE))
    .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyWakeLockTage");
wakeLock.acquire();

Nun kommuniziert dies zu einem schnellen überfliegen "ein WakeLock, dann ist acquirees". Noch einfacher als die erste Version. Es ist sofort klar, dass es sich bei der erworbenen Sache um eine handelt WakeLock. Wenn das Erhalten von PowerManagernur ein Unterdetail ist, das für den Punkt dieses Codes ziemlich unbedeutend ist, aber das wakeLockist wichtig, dann könnte es tatsächlich helfen, das PowerManagerZeug zu vergraben, so dass es natürlich ist, darüber hinweg zu schauen, wenn Sie nur versuchen, schnell ein zu bekommen Vorstellung davon, was dieser Code macht. Und nicht die Namensgebung in Verbindung steht , dass es wird nur einmal verwendet, und manchmal , das ist Was ist wichtig? (Wenn der Rest des Gültigkeitsbereichs sehr lang ist, muss ich alles lesen, um festzustellen, ob er jemals wieder verwendet wird. Die Verwendung expliziter Unterbereiche kann jedoch eine andere Möglichkeit sein, dies zu beheben, sofern Ihre Sprache dies unterstützt.)

Daraus ergibt sich, dass alles vom Kontext und dem, was Sie kommunizieren möchten, abhängt. Wie beim Schreiben von Prosa in natürlicher Sprache gibt es immer viele Möglichkeiten, einen bestimmten Code zu schreiben, die hinsichtlich des Informationsgehalts grundsätzlich gleichwertig sind. Wie beim Schreiben von Prosa in natürlicher Sprache sollten Sie bei der Auswahl der Prosa im Allgemeinen keine mechanistischen Regeln anwenden, wie beispielsweise "lokale Variablen, die nur einmal vorkommen, entfernen". Eher wieWenn Sie sich dazu entschließen, Ihren Code aufzuschreiben, werden bestimmte Dinge hervorgehoben und andere weniger hervorgehoben. Sie sollten sich bemühen, diese Entscheidungen bewusst zu treffen (einschließlich der Entscheidung, aus technischen Gründen manchmal weniger lesbaren Code zu schreiben), basierend auf dem, was Sie tatsächlich hervorheben möchten. Denken Sie vor allem darüber nach, was Lesern helfen soll, die nur den Kern Ihres Codes (auf verschiedenen Ebenen) "verstehen" müssen, da dies weitaus häufiger vorkommt als ein sehr enges Lesen von Ausdruck zu Ausdruck.


Obwohl ich keine Kenntnisse über die API habe, ist es für mich sehr offensichtlich, was der Code auch ohne die Variablen tut. getSystemService(POWER_SERVICE)bekommt den Power Manager. .newWakeLockbekommt eine Wecksperre. .acquireerwirbt es. OK, ich kenne nicht alle Typen, aber ich kann das in der IDE herausfinden, wenn ich muss.
rghome

Es mag für Sie offensichtlich sein, was der Code tun soll , aber Sie haben keine Ahnung, was er tatsächlich tut. Wenn ich nach einem Fehler suche, weiß ich nur, dass ein Code irgendwo nicht das tut, was er tun soll, und ich muss den Code finden.
gnasher729

@ gnasher729 Ja. Die Fehlersuche war ein Beispiel, an dem Sie alle Details sorgfältig durchlesen mussten. Und eines der Dinge, die wirklich helfen, den Code zu finden, der nicht das tut, was er tun soll, ist, leicht erkennen zu können, was der ursprüngliche Autor beabsichtigt hatte.
Ben

Wenn ich wieder herauskomme, habe ich "eine Einheit" vollständig verstanden und kann dann mit der nächsten Aussage fortfahren. Nicht wirklich. Tatsächlich verhindern die lokalen Variablen dieses vollständige Verständnis. Weil sie immer noch verweilen ... den mentalen Raum einnehmen ... was mit ihnen in den nächsten 20 Aussagen geschehen wird ... Trommelwirbel ... Ohne Einheimische wärst du sicher, dass die Objekte verschwunden sind und unmöglich irgendetwas zu tun mit ihnen in den nächsten Zeilen. Keine Notwendigkeit, sich an sie zu erinnern. returnAus dem gleichen Grund möchte ich so schnell wie möglich von Methoden.
Stijn de Witt

2

Dies ist sogar ein Gegenmuster mit eigenem Namen: Train Wreck . Es wurden bereits mehrere Gründe genannt, um den Austausch zu vermeiden:

  • Schwerer zu lesen
  • Schwieriger zu debuggen (sowohl zum Beobachten von Variablen als auch zum Erkennen von Ausnahmepositionen)
  • Verletzung des Gesetzes von Demeter (LoD)

Überlegen Sie, ob diese Methode von anderen Objekten zu viel weiß. Die Verkettung von Methoden ist eine Alternative, mit der Sie die Kopplung verringern können.

Denken Sie auch daran, dass Verweise auf Objekte sehr billig sind.


Ähm, macht der überarbeitete Code nicht Methodenverkettung? BEARBEITEN Sie den verlinkten Artikel, damit ich den Unterschied jetzt sehe. Ziemlich guter Artikel, danke!
Stijn de Witt

Vielen Dank für die Überprüfung. Wie auch immer, in manchen Fällen kann die Verkettung von Methoden funktionieren, während das Verlassen der Variablen die richtige Entscheidung sein kann. Es ist immer gut zu wissen, warum die Leute mit Ihrer Antwort nicht einverstanden sind.
Borjab

Ich denke das Gegenteil von "Train Wreck" ist "Local Line". Es ist dieser Zug zwischen zwei großen Städten, der zwischendurch in jedem Dorf hält, nur für den Fall, dass jemand ein- oder aussteigen möchte, obwohl das an den meisten Tagen niemand tut.
14.

1

Die gestellte Frage lautet "Sollten wir lokale Variablen entfernen, wenn wir können"?

Nein, du solltest nicht ausschließen, nur weil du kannst.

Sie sollten die Codierungsrichtlinien in Ihrem Shop befolgen.

Zum größten Teil erleichtern lokale Variablen das Lesen und Debuggen des Codes.

Mir gefällt, was du damit gemacht hast PowerManager powerManager. Für mich bedeutet Kleinschreibung, dass es sich nur um eine einmalige Verwendung handelt.

Wenn Sie nicht sollten, ist, wenn die Variable teure Ressourcen halten wird. Viele Sprachen haben eine Syntax für eine lokale Variable, die bereinigt / freigegeben werden muss. In C # wird es verwendet.

using(SQLconnection conn = new SQLconnnection())
{
    using(SQLcommand cmd = SQLconnnection.CreateCommand())
    {
    }
}

Dies hängt nicht wirklich mit lokalen Variablen zusammen, sondern mit der Bereinigung von Ressourcen ... Ich kenne mich mit C # nicht gut aus, würde mich aber wundern, wenn using(new SQLConnection()) { /* ... */ }es nicht auch legal wäre (ob es nützlich wäre, ist eine andere Sache :).
Stijn de Witt

1

Ein wichtiger Aspekt wurde in den anderen Antworten nicht erwähnt: Immer wenn Sie eine Variable hinzufügen, führen Sie einen veränderlichen Zustand ein. Dies ist normalerweise eine schlechte Sache, da es Ihren Code komplexer und damit schwieriger zu verstehen und zu testen macht. Natürlich ist das Problem umso kleiner, je kleiner der Gültigkeitsbereich der Variablen ist.

Was Sie wollen, ist eigentlich keine Variable, deren Wert geändert werden kann, sondern eine temporäre Konstante. Aus diesem Grund sollten Sie dies in Ihrem Code ausdrücken, wenn Ihre Sprache dies zulässt. In Java können Sie finalund in C ++ können Sie verwenden const. In den meisten funktionalen Sprachen ist dies das Standardverhalten.

Es ist richtig, dass lokale Variablen der am wenigsten schädliche Typ eines veränderlichen Zustands sind. Mitgliedsvariablen können viel mehr Probleme verursachen und statische Variablen sind noch schlimmer. Trotzdem finde ich es wichtig, in Ihrem Code so genau wie möglich auszudrücken, was er tun soll. Und es gibt einen großen Unterschied zwischen einer Variablen, die später geändert werden könnte, und einem bloßen Namen für ein Zwischenergebnis. Wenn Sie diesen Unterschied in Ihrer Sprache ausdrücken können, tun Sie es einfach.


2
Ich habe Ihre Antwort nicht abgelehnt, aber ich würde vermuten, dass sie mit der Tatsache zusammenhängt, dass lokale Variablen in imperativen Sprachen im Allgemeinen nicht als "Zustand" betrachtet werden, es sei denn, Sie sprechen von einer langfristigen Methode. Ich stimme der Verwendung von final / const als Best Practice zu, aber es ist ziemlich unwahrscheinlich, dass die Einführung einer Variablen zum einfachen Aufbrechen eines verketteten Aufrufs zu Problemen führt, die durch einen veränderlichen Status verursacht werden. Tatsächlich hilft die Verwendung lokaler Variablen beim Umgang mit veränderlichen Zuständen. Wenn eine Methode zu verschiedenen Zeiten unterschiedliche Werte zurückgeben kann, kann es ansonsten zu einigen wirklich interessanten Fehlern kommen.
JimmyJames

@ JimmyJames: Habe meiner Antwort eine Erklärung hinzugefügt. Aber es gibt einen Teil Ihres Kommentars, den ich nicht verstanden habe: "Die Verwendung lokaler Variablen hilft, mit dem veränderlichen Zustand umzugehen". Können Sie das erklären?
Frank Puffer

1
Nehmen wir zum Beispiel an, ich muss einen Wert überprüfen und einen Alarm auslösen, wenn er über 10 liegt. Angenommen, dieser Wert stammt von einer Methode getFoo(). Wenn ich eine lokale Deklaration vermeide, if(getFoo() > 10) alert(getFoo());erhalte ich möglicherweise Aber getFoo () gibt bei zwei verschiedenen Aufrufen möglicherweise unterschiedliche Werte zurück. Ich könnte eine Warnung mit einem Wert kleiner oder gleich 10 senden, was bestenfalls verwirrend ist und als Fehler auf mich zurückkommt. Diese Art von Dingen gewinnt mit der Parallelität an Bedeutung. Die lokale Zuordnung ist atomar.
JimmyJames

Sehr guter Punkt. Vielleicht der Grund, warum ich diese Einheimischen nicht mag ... Wirst du etwas mit dieser PowerManagerInstanz tun , nachdem du diese Methode aufgerufen hast ? Lassen Sie mich den Rest der Methode überprüfen .... mmm ok nein. Und dieses WakeLockDing hast du ... was machst du damit (scannt noch mal den Rest der Methode) ... mmm nochmal nichts ... ok also warum sind sie da? Oh ja, sollte besser lesbar sein ... aber ohne sie wirst du sie sicher nicht mehr benutzen, so dass ich den Rest der Methode nicht lesen muss.
Stijn de Witt

In einem kürzlich gehaltenen Vortrag argumentierte der Redner, dass der beste Weg, um zu zeigen, dass eine Variable endgültig ist , darin besteht, kurze Methoden zu schreiben, bei denen offensichtlich ist, dass sich die Variable nicht ändert . Wenn finaleine lokale Variable in einer Methode benötigt wird, ist Ihre Methode zu lang.
user949300
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.