Ich habe Martin Fowlers Refactoring gelesen . Es ist im Allgemeinen ausgezeichnet, aber eine von Fowlers Empfehlungen scheint ein wenig Ärger zu verursachen.
Fowler empfiehlt, temporäre Variablen durch eine Abfrage zu ersetzen. Stattdessen:
double getPrice() {
final int basePrice = _quantity * _itemPrice;
final double discountFactor;
if (basePrice > 1000) discountFactor = 0.95;
else discountFactor = 0.98;
return basePrice * discountFactor;
}
Sie ziehen sich in eine Hilfsmethode zurück:
double basePrice() {
return _quantity * _itemPrice;
}
double getPrice() {
final double discountFactor;
if (basePrice() > 1000) discountFactor = 0.95;
else discountFactor = 0.98;
return basePrice() * discountFactor;
}
Im Allgemeinen stimme ich zu, außer dass ein Grund, warum ich temporäre Variablen verwende, darin besteht, dass eine Zeile zu lang ist. Zum Beispiel:
$host = 'https://api.twilio.com';
$uri = "$host/2010-04-01/Accounts/$accountSid/Usage/Records/AllTime";
$response = Api::makeRequest($uri);
Wenn ich das einfügen würde, würde die Zeile länger als 80 Zeichen sein.
Alternativ habe ich Codeketten, die selbst nicht viel einfacher zu lesen sind:
$params = MustacheOptions::build(self::flattenParams($bagcheck->getParams()));
Welche Strategien gibt es, um die beiden in Einklang zu bringen?
coding-style
refactoring
variables
Kevin Burke
quelle
quelle
$host
und Ihr$uri
Beispiel ist jedoch irgendwie erfunden - es sei denn, der Host wurde aus einer Einstellung oder einer anderen Eingabe gelesen, ich würde es vorziehen, wenn sie sich in derselben Zeile befinden, selbst wenn sie umbrochen werden oder vom Rand abweichen.Antworten:
Gewusst wie:
1. Es gibt Einschränkungen hinsichtlich der Zeilenlänge, damit Sie mehr Code sehen und verstehen können. Sie sind noch gültig.
2. Betonen Sie das Urteil über blinde Konventionen .
3. Vermeiden Sie temporäre Variablen, es sei denn, Sie optimieren die Leistung .
4. Vermeiden Sie die Verwendung tiefer Einrückungen für die Ausrichtung in mehrzeiligen Anweisungen.
Lange Anweisungen in mehrere Zeilen entlang 5. Pause Idee Grenzen :
Begründung
Die Hauptursache für meine (Debugging-) Probleme mit temporären Variablen ist, dass sie in der Regel veränderlich sind. Ich gehe nämlich davon aus, dass es sich beim Schreiben des Codes um einen Wert handelt. Wenn die Funktion jedoch komplex ist, ändert ein anderer Code ihren Status zur Hälfte. (Oder umgekehrt, wo der Status der Variablen gleich bleibt, sich aber das Ergebnis der Abfrage geändert hat).
Halten Sie sich an Abfragen, es sei denn, Sie optimieren die Leistung . Dadurch bleibt jede Logik, die Sie zur Berechnung dieses Werts verwendet haben, an einem einzigen Ort erhalten.
Die von Ihnen angegebenen Beispiele (Java und ... PHP?) Ermöglichen mehrzeilige Anweisungen. Wenn die Linien lang werden, brechen Sie sie auf. Die jquery-Quelle bringt dies auf die Spitze . (Die erste Anweisung führt zu Zeile 69!) Nicht, dass ich dem unbedingt zustimmen müsste, aber es gibt andere Möglichkeiten, Ihren Code lesbar zu machen, als temporäre Variablen zu verwenden.
Einige Beispiele
1. PEP 8 Style Guide für Python (nicht das schönste Beispiel)
2. Paul M Jones im Pear Style Guide (Argument in der Mitte der Straße)
3. Oracle-Zeilenlänge + Wrapping-Konventionen (nützliche Strategien zur Einhaltung von 80 Zeichen)
4. MDN-Java-Praktiken (betont das Urteil des Programmierers über die Konvention)
quelle
Ich denke, das beste Argument für die Verwendung von Hilfsmethoden anstelle von temporären Variablen ist die menschliche Lesbarkeit. Wenn Sie als Mensch mehr Probleme beim Lesen der Helfer-Methodenkette haben als bei der temporären Varialbe, sehe ich keinen Grund, warum Sie sie extrahieren sollten.
(Bitte korrigiere mich wenn ich falsch liege)
quelle
Ich denke nicht, dass Sie die Richtlinien für 80 Zeichen genau befolgen müssen oder dass jemals eine lokale temporäre Variable extrahiert werden sollte. Es sollten jedoch lange Schlangen und lokale Temperaturen untersucht werden, um die gleiche Idee besser auszudrücken. Grundsätzlich weisen sie darauf hin, dass eine bestimmte Funktion oder Zeile zu kompliziert ist und wir sie zerlegen müssen. Aber wir müssen vorsichtig sein, denn eine schlechte Aufteilung einer Aufgabe macht die Situation nur noch komplizierter. Also soll ich die Dinge in wiederverwendbare und einfache Komponenten zerlegen.
Lassen Sie mich die Beispiele betrachten, die Sie veröffentlicht haben.
Meine Beobachtung ist, dass alle Twilio-API-Aufrufe mit "https://api.twilio.com/2010-04-1/" beginnen und daher eine sehr offensichtliche wiederverwendbare Funktion vorhanden ist:
Tatsächlich denke ich, dass der einzige Grund, eine URL zu generieren, darin besteht, die Anfrage zu stellen, also würde ich Folgendes tun:
Tatsächlich beginnen viele der URLs mit "Accounts / $ accountSid", daher würde ich das wahrscheinlich auch extrahieren:
Und wenn wir die Twilio-API zu einem Objekt machen, das die Kontonummer enthält, können wir Folgendes tun:
Die Verwendung eines $ twilio-Objekts hat den Vorteil, dass Unit-Tests einfacher werden. Ich kann dem Objekt ein anderes $ twilio-Objekt geben, das Twilio nicht zurückruft, was schneller ist und Twilio keine seltsamen Dinge antut.
Schauen wir uns den anderen an
Hier würde ich entweder darüber nachdenken:
oder
oder
Je nachdem, welche Sprache wiederverwendbarer ist.
quelle
Eigentlich bin ich mit dem angesehenen Mr. Fowler im allgemeinen Fall nicht einverstanden.
Der Vorteil des Extrahierens einer Methode aus früher inlineem Code ist die Wiederverwendung von Code. Der Code in der Methode ist jetzt von seiner ursprünglichen Verwendung getrennt und kann jetzt an anderen Stellen im Code verwendet werden, ohne kopiert und eingefügt zu werden (was Änderungen an mehreren Stellen erforderlich machen würde, wenn sich die allgemeine Logik des kopierten Codes jemals ändern müsste). .
Von gleichem, oft größerem konzeptionellen Wert ist jedoch die "Wiederverwendung von Werten". Mr. Fowler nennt diese extrahierten Methoden, um temporäre Variablen zu ersetzen, "Abfragen". Was ist effizienter? eine Datenbank mehrmals abfragen, wenn Sie einen bestimmten Wert benötigen, oder einmal abfragen und das Ergebnis speichern (vorausgesetzt, der Wert ist statisch genug, dass Sie nicht erwarten würden, dass er sich ändert)?
Für fast jede Berechnung, die über die relativ triviale in Ihrem Beispiel hinausgeht, ist es in den meisten Sprachen billiger, das Ergebnis einer Berechnung zu speichern, als es weiter zu berechnen. Daher ist die allgemeine Empfehlung, On-Demand neu zu berechnen, unaufrichtig. Es kostet mehr Entwicklerzeit und mehr CPU-Zeit und spart eine unbedeutende Menge an Speicher, was in den meisten modernen Systemen die billigste Ressource dieser drei ist.
Jetzt könnte die Hilfsmethode in Verbindung mit anderem Code "faul" gemacht werden. Beim ersten Ausführen wird eine Variable initialisiert. Alle weiteren Aufrufe würden diese Variable zurückgeben, bis die Methode explizit angewiesen wurde, neu zu berechnen. Dies kann ein Parameter für die Methode sein oder ein Flag, das von einem anderen Code gesetzt wird und den Wert ändert, von dem die Berechnung dieser Methode abhängt:
Für diese triviale Berechnung wird sogar noch mehr Arbeit geleistet als gespeichert. Daher würde ich generell empfehlen, bei der temporären Variablen zu bleiben. Für komplexere Berechnungen, die Sie im Allgemeinen vermeiden möchten, mehrmals ausgeführt zu werden, und die Sie an mehreren Stellen im Code benötigen, ist dies die Vorgehensweise.
quelle
Hilfsmethoden haben einen Platz, aber Sie müssen vorsichtig sein, um die Konsistenz der Daten und eine unnötige Erweiterung des Variablenumfangs sicherzustellen.
In Ihrem eigenen Beispiel heißt es beispielsweise:
Es ist klar, dass beide
_quantity
und_itemPrice
globale Variablen (oder zumindest Klassenebenen) sind und daher das Potenzial besteht, dass sie außerhalb von geändert werdengetPrice()
Daher besteht die Möglichkeit, dass der erste Aufruf
basePrice()
einen anderen Wert als der zweite Aufruf zurückgibt!Daher würde ich vorschlagen, dass Hilfsfunktionen nützlich sein können, um komplexe Mathematik zu isolieren, aber als Ersatz für lokale Variablen müssen Sie vorsichtig sein.
Sie müssen auch reductio ad absurdum vermeiden - sollte die Berechnung von
discountFactor
auf eine Methode ausgegliedert werden? So wird Ihr Beispiel:Durch die Partitionierung über eine bestimmte Ebene hinaus ist der Code weniger lesbar.
quelle
Wenn Sie zufällig in einer Sprache mit benannten Parametern (ObjectiveC, Python, Ruby usw.) arbeiten, sind temporäre Variablen weniger nützlich.
In Ihrem basePrice-Beispiel kann die Ausführung der Abfrage jedoch eine Weile dauern, und Sie möchten das Ergebnis möglicherweise für die zukünftige Verwendung in einer temporären Variablen speichern.
Wie Sie verwende ich jedoch temporäre Variablen aus Gründen der Klarheit und der Zeilenlänge.
Ich habe auch gesehen, wie Programmierer in PHP Folgendes tun. Es ist interessant und großartig zum Debuggen, aber es ist ein bisschen seltsam.
quelle
Der Grund für diese Empfehlung ist, dass Sie dieselbe Vorberechnung an anderer Stelle in Ihrer Anwendung verwenden möchten. Siehe Ersetzen von Temp durch Abfrage im Refactoring-Musterkatalog:
Daher würde ich in Ihrem Host- und URI-Beispiel diese Empfehlung nur anwenden, wenn ich vorhabe, dieselbe URI- oder Hostdefinition wiederzuverwenden.
Wenn dies aufgrund des Namespace der Fall ist, definiere ich keine globale uri () - oder host () -Methode, sondern einen Namen mit mehr Informationen wie twilio_host () oder archive_request_uri ().
Dann sehe ich für das Problem der Zeilenlänge mehrere Optionen:
uri = archive_request_uri()
.Begründung: In der aktuellen Methode soll der URI der erwähnte sein. Die URI-Definition ist weiterhin faktorisiert.
uri() { return archive_request_uri() }
Wenn Sie häufig die Empfehlung von Fowler verwenden, wissen Sie, dass die uri () -Methode dasselbe Muster aufweist.
Wenn Sie aufgrund der Wahl der Sprache mit einem 'self' auf die lokale Methode zugreifen müssen, würde ich die erste Lösung empfehlen, um die Ausdruckskraft zu erhöhen (in Python würde ich die uri-Funktion in der aktuellen Methode definieren).
quelle