Temporära variabler är bland de vanligaste källorna till onödig komplexitet i programkod. De ackumuleras i långa metoder, ger vaga namn till beräknade värden och gör det svårare att extrahera, testa eller återanvända den logik de innehåller. Refactoring-metoden "Replace Temp with Query", katalogiserad av Martin Fowler i Refactoring: Improving the Design of Existing Code , adresserar detta direkt: istället för att lagra ett beräknat värde i en lokal variabel extraherar du beräkningen till en namngiven metod, en fråga, och anropar den varhelst värdet behövs.
Resultatet är kod som kommunicerar avsikt istället för att dölja den. Beräkningen är inte längre begravd i en variabeltilldelning högst upp i en lång metod; den har ett namn, en plats och möjligheten att testas isolerat. Den här artikeln täcker hela tekniken, vad temporära variabler är, när de blir problem, hur man utför refaktoreringen steg för steg i Java, Python och TypeScript, när man ska tillämpa den och när den inte ska användas, och hur den kopplas till relaterade tekniker i refaktoreringskatalogen.
Omstrukturera din kod med självförtroende
SMART TS XL spår där temporära variabler, duplicerade beräkningar och extraherade frågemetoder används.
Lär dig MERVad är en temporär variabel (Temp) i programmering?
En temporär variabel, vanligtvis kallad temp , är en lokal variabel inuti en funktion eller metod som lagrar ett mellanresultat för användning inom samma scope. Den beräknas en gång, hålls i en namngiven variabel och refereras sedan till senare i samma funktion. Variabeln existerar bara under funktionsanropets livstid; den är inte tillgänglig utanför den och lagras inte i objektets tillstånd.
pytonorm
# 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;
skrivmaskin
// TypeScript: basePrice is a temp variable
const basePrice = quantity * itemPrice; // temp
if (basePrice > 1000) return basePrice * 0.95;
return basePrice * 0.98;
Temporära värden är inte i sig dåliga. De har legitima användningsområden: att fånga resultatet av en dyr operation som skulle vara slösaktig att upprepa, att dela upp en komplex flerstegsberäkning i läsbara steg, eller att lagra värden som ackumuleras över loopiterationer. Problemet uppstår när temporära värden används reflexmässigt för enkla härledda värden som skulle vara tydligare som namngivna metoder, eller när de ackumuleras över en lång metod och tvingar läsare att spåra flera samtidigt aktiva mellanliggande värden.
Vad är refactoring inom programvaruutveckling?
Refactoring är processen att omstrukturera befintlig kod utan att ändra dess observerbara beteende. Målet är att förbättra kodens interna kvalitet: dess läsbarhet, testbarhet, underhållbarhet och modularitet. En refactoring lägger inte till funktioner och åtgärdar inte buggar; den ändrar kodens struktur samtidigt som den bevarar det den gör.
Ersätt Temp med Query är en omstrukturering i en katalog med flera dussin tekniker som beskrivs av Martin Fowler. Den tillhör en familj av tekniker som hanterar metoder som har blivit för långa eller för komplexa:
| Refactoringteknik | Vad den gör |
|---|---|
| Ersätt Temp med Query | Extraherar en temporär variabels beräkning till en namngiven metod |
| Extrahera metod | Extraherar ett kodblock till en ny namngiven metod |
| Inline-temperatur | Ersätter en enkel temp direkt med dess uttryck |
| Dela temporär variabel | Separerar en temperatur som återanvänds för olika ändamål i distinkta variabler |
| Ersätt loop med pipeline | Ersätter en imperativ loop med en funktionell pipeline (mappa, filtrera, minska) |
| Introducera förklarande variabel | Introducerar en namngiven temp för att förtydliga ett komplext uttryck |
Dessa tekniker används inte isolerat. Fowler beskriver att ersätta Temp med Query som ett viktigt steg före Extraheringsmetoden: om en metod har temp-variabler blir det svårt att extrahera en del av den till en ny metod eftersom dessa temp-variabler kan användas både före och efter den extraherade sektionen. Att först eliminera temp-variablerna genom att omvandla dem till frågor banar väg för extraheringen.
Vad är Ersätt Temp med Query?
Ersätt Temp med Query är en refaktoreringsteknik som omvandlar en lokal temporär variabel till ett metodanrop. Istället för att beräkna ett värde och tilldela det till en lokal variabel extraherar du beräkningen till en privat metod, frågan, som returnerar det beräknade värdet när det anropas. Oavsett var temp användes ersätter du referensen med ett anrop till frågemetoden.
Det kanoniska exemplet från Fowlers Refactoring :
Innan:
Java
double basePrice = _quantity * _itemPrice;
if (basePrice > 1000)
return basePrice * 0.95;
else
return basePrice * 0.98;
Efter:
Java
if (basePrice() > 1000)
return basePrice() * 0.95;
else
return basePrice() * 0.98;
private double basePrice() {
return _quantity * _itemPrice;
}
Frågemetoden basePrice() är nu en namngiven, självständig beräkning. Den kan anropas från vilken annan metod som helst i klassen, testas oberoende, åsidosättas i underklasser och förstås utan att först läsa den anropande metoden.
Problemet med temporära variabler
De fragmenterar logik över en metod
En temporär variabel delar upp en beräkning i två separata delar: tilldelningen (där värdet beräknas) och användningen (där det läses). I en kort metod är denna uppdelning ofarlig. I en metod som har vuxit till trettio eller femtio rader kan tilldelningen och användningen separeras av många rader med annan logik. Läsaren måste skrolla upp för att hitta tilldelningen, hålla innebörden i arbetsminnet och skrolla tillbaka till användningen. Varje ytterligare temporär variabel förvärrar denna kognitiva omkostnad.
De blockerar extraktionsmetoden
Det mest betydande praktiska problemet med temps är att de blockerar andra refaktoreringar. Betrakta en metod med en komplex villkorlig gren som skulle dra nytta av att extraheras till sin egen metod. Om grenen använder en temp som tilldelades tidigare i metoden kräver extraheringen antingen att temp skickas som en parameter, att temp görs till en instansvariabel eller att dess värde beräknas igen inuti den extraherade metoden. Inget av dessa alternativ är rent. Att först eliminera temp genom att ersätta den med en fråga tar bort detta hinder helt.
De inbjuder till återanvändning och mutation
Temporära variabler återanvänds ibland för olika ändamål inom samma metod, en metod som Fowler kallar "temporär variabeltrassel". En variabel med namnet temp or result som tilldelas om flera gånger ger ingen semantisk information och vilseleder aktivt läsarna om vad den representerar vid en given tidpunkt. Även engångstemperaturer kan ackumuleras tills en metods omfattning är belamrad med mellanliggande värden som läsarna måste spåra samtidigt.
Steg för steg: Så här använder du Ersätt temporärt med fråga
Transformationen följer fyra steg som kan tillämpas säkert på vilket språk som helst:
Steg 1: Bekräfta att temperaturen tilldelas exakt en gång och aldrig muteras. Om temperaturen tilldelas om senare i metoden, dela upp den först med hjälp av Split Temporary Variable.
Steg 2: Extrahera den högra sidan av uppgiften till en privat metod. Ge metoden ett namn som beskriver vad den beräknar, inte hur. basePrice() är bättre än calculateQuantityTimesPrice().
Steg 3: Ersätt varje referens till temp med ett anrop till den nya metoden. De flesta IDE:er kan göra detta automatiskt: högerklicka på temp → Refactor → Inline Variable, och extrahera sedan Method på det inlineade uttrycket.
Steg 4: Ta bort deklarationen av temp-variabeln. Om extraheringen är klar bör temp-variabeln nu inte ha några referenser och kan tas bort.
Java: Fullständigt fungerande exempel
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;
}
}
Metoden getPrice() läses nu som ett enda uttryck som tydligt kommunicerar beräkningen. Varje extraherad fråga kan läsas, testas och utökas oberoende av varandra. Observera att discountFactor() samtal basePrice(), detta är korrekt eftersom basePrice() är en ren beräkning utan biverkningar, så att anropa den två gånger medför ingen risk.
Python: Ersätt Temp med egenskap
I Python är den naturliga motsvarigheten till en frågemetod en @property, vilket gör att metoden kan anropas utan parenteser och läser identiskt med ett attribut access:
pytonorm
# 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
pytonorm
# 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
Använda @property betyder self.base_price läser identiskt med en instansvariabel, vilket gör att den anropande koden self.base_price * self.discount_factor helt naturligt. Varje egenskap kan testas oberoende av varandra:
pytonorm
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
Denna nivå av testbarhet är omöjlig med den temporära versionen: de interna beräkningarna base_price och discount_factor är inte åtkomliga utifrån metoden.
TypeScript: Frågemetoder och Getters
TypeScript stöder både metodbaserade frågor och egenskapsgetters, vilket matchar de mönster som finns tillgängliga i Java respektive Python:
skrivmaskin
// 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;
}
}
skrivmaskin
// 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;
}
}
Namnge frågemetoder väl
Frågemetodens namn gör det viktigaste arbetet. En dåligt namngiven extrahering är värre än den temporära metoden den ersatte, eftersom den skapar en ogenomskinlig indirektion: anropare måste navigera till metoddefinitionen för att förstå vad den gör, vilket motverkar syftet.
Bra namn på frågemetoder följer dessa principer:
Ange vad det representerar, inte hur det beräknas. basePrice() kommunicerar affärsidén. getQuantityTimesItemPrice() beskriver beräkningen, inte konceptet. Skillnaden spelar roll när beräkningen ändras, konceptnamnet basePrice() förblir stabil även om formeln ändras.
Använd substantivfraser för värden. Frågemetoder returnerar värden; de är inte kommandon. discountFactor(), totalAmount(), isEligible() följ konventionen att namnge det som returneras. calculateDiscount(), processAmount() följ den imperativa konventionen för kommandon, vilket är förvirrande för metoder som enbart beräknar och returnerar.
Booleska frågor ska läsas som frågor. isHighValue(), hasDiscount(), meetsThreshold() kommunicera att returen är en boolesk fråga och att den som anropar ställer en ja/nej-fråga. bool 変数名 (boolesk variabelnamngivning, en fråga i Search Console-data) återspeglar just denna oro: booleska variabler och metoder behöver namn som gör deras betydelse tydlig vid användningstillfället.
Håll namnen stabila över relaterade metoder. If basePrice() används av discountFactor(), namngivningskonsekvensen visar läsarna att discountFactor beror på basePriceInkonsekvent namngivning bryter mot denna implicita dokumentation.
När ska man tillämpa Ersätt tillfälligt med fråga
Tillämpa denna omstrukturering när:
- Temperaturen tilldelas exakt en gång och tilldelas aldrig om
- Beräkningen är ett rent uttryck: den läser från fält eller parametrar men modifierar inte externt tillstånd, anropar inte nätverkstjänster eller är beroende av tid eller slumpmässighet.
- Beräkningen är tillräckligt komplex för att namngivningen skulle underlätta läsbarheten, eller tillräckligt enkel för att temperaturen bara är rörig.
- Du håller på att tillämpa extraktionsmetoden på ett block som använder den temp.
Det vanligaste idealscenariot är ett härlett värde: ett pris, en totalsumma, en rabatt, en formaterad sträng, en villkorlig klassificering. Dessa är värden som härleds helt och hållet från objektets fält, utan bieffekter, och som naturligt hör hemma som egenskaper hos objektet snarare än som mellanliggande beräkningar inuti en metod.
När det inte ska tillämpas Ersätt Temp med Query
Prestandakänsliga operationer. Om beräkningen är dyr, en databasfråga, ett nätverksanrop, en O(n²)-loop, eller två anrop av frågemetoden, medför dubbel kostnad. Temp-funktionen finns just för att undvika detta. I dessa fall, antingen lämna temp-funktionen kvar eller memorera frågemetoden (cacha resultatet efter det första anropet):
pytonorm
# 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
Biverkande åtgärder. Om den tillfälliga åtgärden innehåller resultatet av en åtgärd som bara ska köras en gång (generera ett unikt ID, logga, skriva till en fil), kommer åtgärden att köras vid varje anrop om den omvandlas till en fråga. Detta ändrar programmets beteende, inte bara dess struktur. Använd inte denna omstrukturering på biverkande tillfälliga åtgärden.
Temporer som ackumuleras över loop-iterationer. En temperatur som är ackumulatorn i en for slinga, total += item.price, är inte en kandidat för Ersätt temporär med fråga. Det är inte ett härlett värde; det är ett tillstånd som byggs upp över iterationer. Överväg istället att ersätta loop med pipeline om loopen är problemet.
Relaterade refactoringtekniker
Ersätt Temp med Query tillhör en familj av tekniker som tillsammans eliminerar onödig komplexitet i metoder. Att förstå familjen hjälper utvecklare att välja rätt teknik för det aktuella problemet:
Extract Method är den vanligaste följeslagaren. Att ersätta Temp med Query gör ofta Extract Method möjlig genom att rensa variabler som annars skulle kräva besvärlig parameteröverföring mellan den extraherade delen och resten av metoden.
Inline Temp är motsatsen till att introducera en temp: den ersätter en temp med dess uttryck direkt i koden. Använd Inline Temp när temp inte ger någon tydlighet och dess uttryck redan är läsbart.
Dela temporär variabel tillämpas när en enskild temp återanvänds för flera ändamål med samma metod. Dela upp den i separata variabler med namn som återspeglar varje syfte och tillämpa sedan Ersätt temp med Query på någon av de resulterande engångstemparna.
Att introducera en variabel som förklarar detta är i motsatt riktning: om ett komplext uttryck är svårt att läsa kan introduktionen av en temp med ett beskrivande namn förbättra tydligheten. Denna teknik och Ersätt Temp med Query står i konflikt och utvecklaren måste bedöma vilken riktning som förbättrar den specifika koden i fråga.
Ersätt loop med pipeline adresserar ett vanligt mönster där en loop med en tempackumulator kan ersättas med en kedjad pipeline-operation (map, filter, reduce), vilket är mer deklarativt och lättare att läsa.
Hur SMART TS XL Stöder omstrukturering i stor skala
"Replace Temp with Query" är en lokal refaktorering: den transformerar en variabel i en metod. I en kodbas av någon större storlek är den mer användbara frågan inte "hur tillämpar jag denna refaktorering?" utan "var i hela kodbasen ska jag tillämpa den, och vad kommer att påverkas när jag gör det?"
SMART TS XL tillhandahåller den strukturella analys över flera kodbaser som gör det systematiskt att besvara denna fråga. Den identifierar var samma beräkning utförs som en temporär variabel på flera platser, det mönster som Ersätt Temp med Query är utformat för att konsolidera till en enda namngiven frågemetod. Den spårar hur en omstrukturerad frågemetod används när den extraheras, vilket gör omfattningen av omstruktureringen synlig innan den görs. Och den fungerar över flera språk: för företagssystem där COBOL-program, Java-tjänster och Python-pipelines alla arbetar med samma data, statisk kodanalys och konsekvensanalys identifiera var samma logiska beräkning förekommer i olika former på olika språk, den djupare formen av problemet som Ersätt temporär med fråga adresserar på enspråkig nivå.
För team som arbetar med äldre modernisering, SMART TS XLÄr visualisering av beroenden gör det möjligt att se var omstrukturerade komponenter används innan de ändras, vilket säkerställer att extrahering av en beräkning till en frågemetod inte förstör anropare som förväntade sig den ursprungliga strukturen.
Temporära variabler och självdokumenterande kod
Beslutet att ersätta en temp med en fråga är i slutändan ett beslut om vad koden ska kommunicera. Temporära variabler kommunicerar implementering: detta är beräkningen jag utförde för att få detta värde. Frågemetoder kommunicerar domän: detta är vad detta värde betyder. I en Order-klass, basePrice() berättar för läsaren att detta koncept existerar inom domänen. double x = quantity * itemPrice berättar för läsaren om en aritmetisk operation.
Allt eftersom kod utvecklas behöver domänkoncept stabila hem. En beräkning inbäddad i en temp-metod kan ändras, dupliceras med flera metoder eller missförstås av nästa utvecklare som läser den. En namngiven frågetod kan hittas, testas, dokumenteras och utvecklas avsiktligt. Den stabiliteten, över alla platser där beräkningen behövs och över alla utvecklare som kommer att arbeta med den, är det som gör Ersätt Temp med Query till mer än en syntaxändring. Det är ett beslut om hur kodbasen kommunicerar problemet den löser.