Temporäre Variablen gehören zu den häufigsten Ursachen unnötiger Komplexität in Softwarecode. Sie häufen sich in langen Methoden, geben berechneten Werten unklare Namen und erschweren das Extrahieren, Testen und Wiederverwenden der darin enthaltenen Logik. Das von Martin Fowler in „ Refactoring: Improving the Design of Existing Code“ beschriebene Refactoring-Verfahren „Temporäre Variablen durch Abfragen ersetzen “ setzt genau hier an: Anstatt einen berechneten Wert in einer lokalen Variable zu speichern, wird die Berechnung in eine benannte Methode, eine Abfrage, ausgelagert und diese überall dort aufgerufen, wo der Wert benötigt wird.
Das Ergebnis ist Code, der die Absicht klar kommuniziert, anstatt sie zu verbergen. Die Berechnung ist nicht länger in einer Variablendeklaration am Anfang einer langen Methode versteckt; sie hat einen Namen, einen Speicherort und kann isoliert getestet werden. Dieser Artikel behandelt die gesamte Technik, was temporäre Variablen sind, wann sie problematisch werden, wie man das Refactoring Schritt für Schritt in Java, Python und TypeScript durchführt, wann es angewendet werden sollte und wann nicht, und wie es mit verwandten Techniken im Refactoring-Katalog zusammenhängt.
Refaktorieren Sie Ihren Code mit Zuversicht
SMART TS XL Spuren, wo temporäre Variablen, doppelte Berechnungen und extrahierte Abfragemethoden verwendet werden.
Erfahren Sie mehrWas ist eine temporäre Variable (Temp) in der Programmierung?
Eine temporäre Variable, kurz „ temp“ , ist eine lokale Variable innerhalb einer Funktion oder Methode, die ein Zwischenergebnis für die Verwendung innerhalb desselben Gültigkeitsbereichs speichert. Sie wird einmal berechnet, in einer benannten Variable gespeichert und später in derselben Funktion referenziert. Die Variable existiert nur für die Dauer des Funktionsaufrufs; sie ist außerhalb davon nicht zugänglich und wird nicht im Zustand des Objekts gespeichert.
python
# Python: base_price is a temp variable
def calculate_total(quantity, item_price):
base_price = quantity * item_price # temp: computed once, used below
if base_price > 1000:
return base_price * 0.95
return base_price * 0.98
Java
// Java: basePrice is a temp variable
double basePrice = quantity * itemPrice; // temp
if (basePrice > 1000) {
return basePrice * 0.95;
}
return basePrice * 0.98;
Typoskript
// TypeScript: basePrice is a temp variable
const basePrice = quantity * itemPrice; // temp
if (basePrice > 1000) return basePrice * 0.95;
return basePrice * 0.98;
Temporale Variablen sind nicht grundsätzlich schlecht. Sie haben durchaus ihre Berechtigung: Sie erfassen das Ergebnis einer aufwändigen Operation, deren Wiederholung unnötig wäre, unterteilen komplexe, mehrstufige Berechnungen in übersichtliche Abschnitte oder speichern Werte, die sich über Schleifendurchläufe hinweg akkumulieren. Problematisch wird es jedoch, wenn temporäre Variablen reflexartig für einfache abgeleitete Werte verwendet werden, die als benannte Methoden verständlicher wären, oder wenn sie sich über eine lange Methode akkumulieren und Leser zwingen, mehrere gleichzeitig aktive Zwischenwerte zu verfolgen.
Was ist Refactoring in der Softwareentwicklung?
Refactoring ist der Prozess der Umstrukturierung bestehenden Codes, ohne dessen beobachtbares Verhalten zu verändern. Ziel ist die Verbesserung der internen Codequalität: Lesbarkeit, Testbarkeit, Wartbarkeit und Modularität. Ein Refactoring fügt keine neuen Funktionen hinzu und behebt keine Fehler; es verändert die Codestruktur, ohne die Funktionalität zu beeinträchtigen.
Das Ersetzen von Temp durch Query ist eine von mehreren Dutzend Refactoring-Techniken, die von Martin Fowler beschrieben werden. Sie gehört zu einer Familie von Techniken, die sich mit Methoden befassen, die zu lang oder zu komplex geworden sind:
| Refactoring-Technik | Was es macht |
|---|---|
| Ersetzen Sie Temp durch Query | Extrahiert die Berechnung einer temporären Variable in eine benannte Methode. |
| Extraktionsmethode | Extrahiert einen Codeblock in eine neue benannte Methode |
| Inline-Temperatur | Ersetzt eine einfache Temperaturangabe direkt durch ihren Ausdruck. |
| Temporäre Variable teilen | Teilt eine für verschiedene Zwecke wiederverwendete temporäre Variable in unterschiedliche Variablen auf. |
| Ersetze Schleife durch Pipeline | Ersetzt eine imperative Schleife durch eine funktionale Pipeline (map, filter, reduce). |
| Einführung einer erklärenden Variable | Führt eine benannte temporäre Variable ein, um einen komplexen Ausdruck zu verdeutlichen. |
Diese Techniken werden nicht isoliert angewendet. Fowler beschreibt das Ersetzen von temporären Variablen durch Abfragen als einen entscheidenden Schritt vor dem Extrahieren von Methoden: Enthält eine Methode temporäre Variablen, gestaltet sich das Extrahieren eines Teils davon in eine neue Methode schwierig, da diese temporären Variablen sowohl vor als auch nach dem extrahierten Abschnitt verwendet werden können. Durch das vorherige Eliminieren der temporären Variablen, indem man sie in Abfragen umwandelt, wird der Weg für die Extraktion frei.
Was bedeutet „Temp durch Query ersetzen“?
Die Refactoring-Technik „Temp durch Query ersetzen“ wandelt eine lokale temporäre Variable in einen Methodenaufruf um. Anstatt einen Wert zu berechnen und ihn einer lokalen Variable zuzuweisen, wird die Berechnung in eine private Methode, die sogenannte Abfrage, ausgelagert, die beim Aufruf den berechneten Wert zurückgibt. Überall dort, wo zuvor eine temporäre Variable verwendet wurde, wird der Verweis auf diese Variable durch einen Aufruf der Abfragemethode ersetzt.
Das kanonische Beispiel aus Fowlers Refactoring :
Vorher:
Java
double basePrice = _quantity * _itemPrice;
if (basePrice > 1000)
return basePrice * 0.95;
else
return basePrice * 0.98;
Nach:
Java
if (basePrice() > 1000)
return basePrice() * 0.95;
else
return basePrice() * 0.98;
private double basePrice() {
return _quantity * _itemPrice;
}
Die Abfragemethode basePrice() ist nun eine benannte, in sich abgeschlossene Berechnung. Sie kann von jeder anderen Methode der Klasse aufgerufen, unabhängig getestet, in Unterklassen überschrieben und verstanden werden, ohne dass die aufrufende Methode vorher gelesen werden muss.
Das Problem mit temporären Variablen
Sie fragmentieren die Logik über eine Methode hinweg.
Eine temporäre Variable teilt eine Berechnung in zwei separate Teile: die Zuweisung (wo der Wert berechnet wird) und die Verwendung (wo er gelesen wird). In einer kurzen Methode ist diese Aufteilung unproblematisch. In einer Methode mit dreißig oder fünfzig Zeilen Code können Zuweisung und Verwendung jedoch durch viele weitere Codezeilen getrennt sein. Der Leser muss nach oben scrollen, um die Zuweisung zu finden, deren Bedeutung im Kurzzeitgedächtnis behalten und dann wieder zurück zur Verwendung scrollen. Jede zusätzliche temporäre Variable erhöht diesen kognitiven Aufwand.
Sie blockieren die Extraktionsmethode
Das größte praktische Problem mit temporären Variablen (temp) besteht darin, dass sie andere Refactorings blockieren. Stellen Sie sich eine Methode mit einer komplexen bedingten Verzweigung vor, die von einer Auslagerung in eine eigene Methode profitieren würde. Verwendet die Verzweigung eine temporäre Variable, die zuvor in der Methode zugewiesen wurde, erfordert die Auslagerung entweder die Übergabe der temporären Variable als Parameter, die Deklaration der temporären Variable als Instanzvariable oder die erneute Berechnung ihres Wertes innerhalb der ausgelagerten Methode. Keine dieser Optionen ist elegant. Die temporäre Variable kann jedoch durch eine Abfrage ersetzt werden, wodurch dieses Problem vollständig beseitigt wird.
Sie laden zur Wiederverwendung und Veränderung ein.
Temporäre Variablen werden manchmal innerhalb derselben Methode für verschiedene Zwecke wiederverwendet, eine Praxis, die Fowler als „temporäres Variablengewirr“ bezeichnet. Eine Variable namens temp or result Eine mehrfach neu zugewiesene Variable liefert keine semantische Information und führt die Leser aktiv in die Irre, was sie zu einem bestimmten Zeitpunkt repräsentiert. Selbst temporäre Variablen mit nur einem Zweck können sich so weit anhäufen, dass der Gültigkeitsbereich einer Methode mit Zwischenwerten überladen ist, die die Leser gleichzeitig verfolgen müssen.
Schritt für Schritt: So wenden Sie „Temp durch Abfrage ersetzen“ an
Die Transformation erfolgt in vier Schritten, die in jeder Sprache bedenkenlos angewendet werden können:
Schritt 1: Stellen Sie sicher, dass die temporäre Variable genau einmal zugewiesen und nicht verändert wird. Falls die temporäre Variable später in der Methode neu zugewiesen wird, teilen Sie sie vorher mit der Methode „Temporäre Variable teilen“.
Schritt 2: Extrahieren Sie die rechte Seite der Zuweisung in eine private Methode. Geben Sie der Methode einen Namen, der beschreibt, was sie berechnet, nicht wie. basePrice() ist besser als calculateQuantityTimesPrice().
Schritt 3: Ersetzen Sie jeden Verweis auf die temporäre Variable durch einen Aufruf der neuen Methode. Die meisten IDEs können dies automatisch erledigen: Klicken Sie mit der rechten Maustaste auf die temporäre Variable → Refactoring → Variable inline einbinden und anschließend die Methode des eingebundenen Ausdrucks extrahieren.
Schritt 4: Löschen Sie die Deklaration der temporären Variable. Wenn die Extraktion abgeschlossen ist, sollte die temporäre Variable nun keine Referenzen mehr enthalten und kann entfernt werden.
Java: Vollständiges lauffähiges Beispiel
Java
// Before: Order class with temporary variables
public class Order {
private int quantity;
private double itemPrice;
public double getPrice() {
double basePrice = quantity * itemPrice; // temp 1
double discountFactor; // temp 2
if (basePrice > 1000)
discountFactor = 0.95;
else
discountFactor = 0.98;
return basePrice * discountFactor;
}
}
Java
// After: temps extracted to query methods
public class Order {
private int quantity;
private double itemPrice;
public double getPrice() {
return basePrice() * discountFactor();
}
private double basePrice() {
return quantity * itemPrice;
}
private double discountFactor() {
return basePrice() > 1000 ? 0.95 : 0.98;
}
}
Das Verfahren getPrice() Es liest sich nun als ein einziger Ausdruck, der die Berechnung klar darstellt. Jede extrahierte Abfrage kann unabhängig gelesen, getestet und erweitert werden. Beachten Sie, dass discountFactor() Anrufe basePrice()Das ist richtig, weil basePrice() Es handelt sich um eine reine Berechnung ohne Nebenwirkungen, daher birgt ein zweimaliger Aufruf kein Risiko.
Python: Temp durch Property ersetzen
In Python ist das natürliche Äquivalent einer Abfragemethode eine @propertyDadurch kann die Methode ohne Klammern aufgerufen werden und liest sich identisch zu einem Attributzugriff:
python
# Before: temporary variables in a method
class Order:
def __init__(self, quantity, item_price):
self.quantity = quantity
self.item_price = item_price
def get_price(self):
base_price = self.quantity * self.item_price # temp
discount = 0.95 if base_price > 1000 else 0.98 # temp
return base_price * discount
python
# After: temps replaced with properties (query methods in Python)
class Order:
def __init__(self, quantity, item_price):
self.quantity = quantity
self.item_price = item_price
def get_price(self):
return self.base_price * self.discount_factor
@property
def base_price(self):
return self.quantity * self.item_price
@property
def discount_factor(self):
return 0.95 if self.base_price > 1000 else 0.98
Die Verwendung von @property Mittel verbinden self.base_price wird identisch zu einer Instanzvariable gelesen, wodurch der aufrufende Code self.base_price * self.discount_factor völlig natürlich. Jede Eigenschaft ist einzeln überprüfbar:
python
def test_base_price():
order = Order(10, 150)
assert order.base_price == 1500
def test_discount_factor_high_value():
order = Order(10, 150) # base_price = 1500 > 1000
assert order.discount_factor == 0.95
def test_get_price():
order = Order(10, 150)
assert order.get_price() == 1500 * 0.95
Diese Testbarkeit ist mit der temp-basierten Version nicht möglich: die internen Berechnungen base_price und discount_factor sind von außerhalb der Methode nicht zugänglich.
TypeScript: Abfragemethoden und Getter
TypeScript unterstützt sowohl methodenbasierte Abfragen als auch Property-Getter, entsprechend den in Java bzw. Python verfügbaren Mustern:
Typoskript
// Before: temporary variables
class Order {
constructor(private quantity: number, private itemPrice: number) {}
getPrice(): number {
const basePrice = this.quantity * this.itemPrice; // temp
const discount = basePrice > 1000 ? 0.95 : 0.98; // temp
return basePrice * discount;
}
}
Typoskript
// After: TypeScript getters replace temps
class Order {
constructor(private quantity: number, private itemPrice: number) {}
getPrice(): number {
return this.basePrice * this.discountFactor;
}
private get basePrice(): number {
return this.quantity * this.itemPrice;
}
private get discountFactor(): number {
return this.basePrice > 1000 ? 0.95 : 0.98;
}
}
Benennung von Abfragemethoden
Der Name der Abfragemethode ist von größter Bedeutung. Eine schlecht benannte Extraktion ist schlimmer als die temporäre Variable, die sie ersetzt hat, da sie eine undurchsichtige Umleitung erzeugt: Aufrufer müssen zur Methodendefinition navigieren, um deren Funktion zu verstehen, was den Zweck verfehlt.
Gute Namen für Abfragemethoden folgen diesen Prinzipien:
Benennen Sie, was es darstellt, nicht, wie es berechnet wird. basePrice() vermittelt das Geschäftskonzept. getQuantityTimesItemPrice() Beschreibt die Berechnung, nicht das Konzept. Die Unterscheidung ist wichtig, wenn sich die Berechnung ändert, der Begriff aber auch. basePrice() bleibt auch bei einer Änderung der Formel stabil.
Verwenden Sie Nominalphrasen für Werte. Abfragemethoden geben Werte zurück; sie sind keine Befehle. discountFactor(), totalAmount(), isEligible() Beachten Sie die Konvention zur Benennung des Rückgabewerts. calculateDiscount(), processAmount() Die Befehle folgen der imperativen Konvention, was für Methoden, die lediglich berechnen und zurückgeben, verwirrend ist.
Boolesche Abfragen sollten als Fragen gelesen werden. isHighValue(), hasDiscount(), meetsThreshold() Vermitteln Sie, dass der Rückgabewert ein boolescher Wert ist und dass der Aufrufer eine Ja/Nein-Frage stellt. bool 変数名 (Die Benennung von Booleschen Variablen, eine Abfrage in den Search Console-Daten) spiegelt genau diese Problematik wider: Boolesche Variablen und Methoden benötigen Namen, die ihre Bedeutung zum Zeitpunkt der Verwendung klar machen.
Sorgen Sie für einheitliche Namen über verwandte Methoden hinweg. If basePrice() wird verwendet von discountFactor()Die einheitliche Namensgebung signalisiert den Lesern, dass discountFactor hängt basePriceEine uneinheitliche Namensgebung führt zu einem Bruch dieser impliziten Dokumentation.
Wann sollte „Temp durch Abfrage ersetzen“ angewendet werden?
Wenden Sie dieses Refactoring an, wenn:
- Die Zeitarbeitskraft wird genau einmal zugewiesen und nie wieder neu zugewiesen.
- Die Berechnung ist ein reiner Ausdruck: Sie liest aus Feldern oder Parametern, verändert aber keinen externen Zustand, ruft keine Netzwerkdienste auf und ist weder von der Zeit noch vom Zufall abhängig.
- Die Berechnung ist entweder so komplex, dass eine Benennung die Lesbarkeit verbessern würde, oder so einfach, dass die Angabe der Temperatur nur unnötigen Ballast darstellt.
- Sie sind im Begriff, die Methode „Extract“ auf einen Block anzuwenden, der die temporären Daten verwendet.
Das häufigste ideale Szenario ist ein abgeleiteter Wert: ein Preis, ein Gesamtbetrag, ein Rabatt, eine formatierte Zeichenkette, eine bedingte Klassifizierung. Dies sind Werte, die ausschließlich aus den Feldern des Objekts abgeleitet werden, ohne Seiteneffekte, und die naturgemäß als Eigenschaften des Objekts und nicht als Zwischenberechnungen innerhalb einer Methode zu verstehen sind.
Wann sollte man „Temp durch Abfrage ersetzen“ nicht anwenden?
Leistungskritische Operationen. Bei rechenintensiven Vorgängen wie Datenbankabfragen, Netzwerkaufrufen oder O(n²)-Schleifen verdoppelt sich der Aufwand durch einen doppelten Aufruf der Abfragemethode. Die temporäre Variable dient genau diesem Zweck. In solchen Fällen sollte die temporäre Variable entweder beibehalten oder die Abfragemethode memoisiert werden (das Ergebnis nach dem ersten Aufruf zwischenspeichern).
python
# Memoized property: computed once, cached
from functools import cached_property
class Order:
@cached_property
def expensive_validation(self):
return self.external_service.validate(self.data) # called once, cached
Operationen mit Seiteneffekten. Enthält eine temporäre Variable das Ergebnis einer Operation, die nur einmal ausgeführt werden soll (z. B. Generierung einer eindeutigen ID, Protokollierung, Schreiben in eine Datei), würde die Umwandlung in eine Abfrage die Operation bei jedem Aufruf ausführen. Dies ändert das Verhalten des Programms, nicht nur seine Struktur. Wenden Sie diese Refaktorisierung nicht auf temporäre Variablen mit Seiteneffekten an.
Temperaturen, die sich über die Schleifeniterationen hinweg akkumulieren. Eine Temperatur, die der Akkumulator in einem for Schleife, total += item.priceist kein Kandidat für „Temp durch Abfrage ersetzen“. Es handelt sich nicht um einen abgeleiteten Wert, sondern um einen Zustand, der sich über Iterationen hinweg aufbaut. Erwägen Sie stattdessen „Schleife durch Pipeline ersetzen“, falls die Schleife das Problem verursacht.
Verwandte Refactoring-Techniken
Die Ersetzung von Temp durch Query gehört zu einer Gruppe von Techniken, die gemeinsam unnötige Komplexität in Methoden eliminieren. Das Verständnis dieser Technikgruppe hilft Entwicklern, die richtige Technik für das jeweilige Problem auszuwählen.
Die Methode `Extract` ist die am häufigsten verwendete Ergänzung. Die Option `Temp` durch `Query` ermöglicht oft die Verwendung von `Extract`, indem Variablen gelöscht werden, die andernfalls eine umständliche Parameterübergabe zwischen dem extrahierten Teil und dem Rest der Methode erfordern würden.
Inline Temp ist das Gegenteil von temporären Variablen: Es ersetzt eine temporäre Variable direkt im Code durch ihren Ausdruck. Verwenden Sie Inline Temp, wenn die temporäre Variable keine zusätzliche Lesbarkeit bietet und ihr Ausdruck bereits gut lesbar ist.
Die Option „Temporäre Variable aufteilen“ kommt zum Einsatz, wenn eine einzelne temporäre Variable in derselben Methode für mehrere Zwecke wiederverwendet wird. Teilen Sie sie in separate Variablen mit Namen auf, die den jeweiligen Zweck widerspiegeln, und wenden Sie anschließend die Option „Temporäre Variable durch Abfrage ersetzen“ auf alle resultierenden, nur einmal verwendbaren temporären Variablen an.
Das Einfügen einer erklärenden Variable ist der umgekehrte Weg: Wenn ein komplexer Ausdruck schwer lesbar ist, kann die Einführung einer temporären Variable mit einem beschreibenden Namen die Lesbarkeit verbessern. Diese Technik und das Ersetzen der temporären Variable durch eine Abfrage stehen im Widerspruch zueinander, und der Entwickler muss entscheiden, welche Methode den jeweiligen Code verbessert.
Ersetze Schleife durch Pipeline behandelt ein häufiges Muster, bei dem eine Schleife mit einem Temperaturakkumulator durch eine verkettete Pipeline-Operation ersetzt werden kann (map, filter, reduce), was aussagekräftiger und leichter lesbar ist.
Wie SMART TS XL Unterstützt Refactoring in großem Umfang
Das Ersetzen von Temp durch Query ist ein lokales Refactoring: Es transformiert eine Variable in einer Methode. In einer größeren Codebasis ist die wichtigere Frage nicht „Wie wende ich dieses Refactoring an?“, sondern „Wo in der gesamten Codebasis sollte ich es anwenden und welche Auswirkungen hat das?“
SMART TS XL Es bietet die codeübergreifende Strukturanalyse, die eine systematische Beantwortung dieser Frage ermöglicht. Es identifiziert Stellen, an denen dieselbe Berechnung mit einer temporären Variable an mehreren Stellen durchgeführt wird – ein Muster, das „Temp durch Query ersetzen“ in einer einzigen benannten Abfragemethode zusammenfassen soll. Es verfolgt die Verwendung einer refaktorierten Abfragemethode nach ihrer Extraktion und macht so den Umfang der Refaktorisierung sichtbar, bevor sie durchgeführt wird. Und es funktioniert sprachübergreifend: Für Unternehmenssysteme, in denen COBOL-Programme, Java-Dienste und Python-Pipelines alle mit denselben Daten arbeiten, statische Code-Analyse und Wirkungsanalyse Identifizieren, wo die gleiche logische Berechnung in verschiedenen Sprachen in unterschiedlichen Formen auftritt, die tieferliegende Form des Problems, das Replace Temp with Query auf der Ebene einzelner Sprachen adressiert.
Für Teams, die an Folgendem arbeiten Modernisierung des Altbestands, SMART TS XL Abhängigkeitsvisualisierung Dadurch wird es möglich, vor der Änderung zu sehen, wo refaktorierte Komponenten verwendet werden, und es wird sichergestellt, dass das Auslagern einer Berechnung in eine Abfragemethode keine Aufrufer beeinträchtigt, die die ursprüngliche Struktur erwartet haben.
Temporäre Variablen und selbstdokumentierender Code
Die Entscheidung, eine temporäre Variable durch eine Abfrage zu ersetzen, ist letztendlich eine Entscheidung darüber, was der Code vermitteln soll. Temporäre Variablen kommunizieren die Implementierung: Dies ist die Berechnung, die ich durchgeführt habe, um diesen Wert zu erhalten. Abfragemethoden kommunizieren die Domäne: Dies ist die Bedeutung dieses Wertes. In einer Order-Klasse, basePrice() teilt dem Leser mit, dass dieses Konzept in dem betreffenden Bereich existiert. double x = quantity * itemPrice erklärt dem Leser eine arithmetische Operation.
Mit der Weiterentwicklung von Code benötigen Domänenkonzepte stabile Strukturen. Eine in einer temporären Variable eingebettete Berechnung kann sich ändern, in mehreren Methoden dupliziert werden oder vom nächsten Entwickler, der sie liest, missverstanden werden. Eine benannte Abfragemethode hingegen kann gefunden, getestet, dokumentiert und gezielt weiterentwickelt werden. Diese Stabilität – an allen Stellen, an denen die Berechnung benötigt wird, und für alle Entwickler, die damit arbeiten – macht „Temp durch Query ersetzen“ zu mehr als nur einer Syntaxänderung. Es ist eine Entscheidung darüber, wie die Codebasis das Problem, das sie löst, kommuniziert.