Midlertidige variabler er blandt de mest almindelige kilder til unødvendig kompleksitet i softwarekode. De akkumuleres i lange metoder, giver vage navne til beregnede værdier og gør det sværere at udtrække, teste eller genbruge den logik, de indeholder. Refactoring-metoden "Replace Temp with Query", katalogiseret af Martin Fowler i Refactoring: Improving the Design of Existing Code , adresserer dette direkte: i stedet for at gemme en beregnet værdi i en lokal variabel, udtrækker du beregningen til en navngivet metode, en forespørgsel, og kalder den, hvor værdien er nødvendig.
Resultatet er kode, der kommunikerer intention i stedet for at skjule den. Beregningen er ikke længere begravet i en variabeltildeling øverst i en lang metode; den har et navn, en placering og muligheden for at blive testet isoleret. Denne artikel dækker den komplette teknik, hvad midlertidige variabler er, hvornår de bliver problemer, hvordan man udfører refactoring trin for trin i Java, Python og TypeScript, hvornår den skal anvendes, og hvornår den ikke skal anvendes, og hvordan den forbinder sig med relaterede teknikker i refactoringkataloget.
Refaktorér din kode med selvtillid
SMART TS XL spor, hvor midlertidige variabler, duplikerede beregninger og udtrukne forespørgselsmetoder anvendes.
Lær mereHvad er en midlertidig variabel (Temp) i programmering?
En midlertidig variabel, almindeligvis kaldet en temp , er en lokal variabel i en funktion eller metode, der lagrer et mellemresultat til brug inden for det samme omfang. Den beregnes én gang, opbevares i en navngiven variabel og refereres derefter til senere i den samme funktion. Variablen eksisterer kun i funktionskaldets levetid; den er ikke tilgængelig uden for den og lagres ikke i objektets tilstand.
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;
maskinskrift
// TypeScript: basePrice is a temp variable
const basePrice = quantity * itemPrice; // temp
if (basePrice > 1000) return basePrice * 0.95;
return basePrice * 0.98;
Midlertidige værdier er ikke i sig selv dårlige. De har legitime anvendelser: at registrere resultatet af en dyr operation, der ville være spild af at gentage, at opdele en kompleks flertrinsberegning i læsbare faser eller at fastholde værdier, der akkumuleres på tværs af loop-iterationer. Problemet opstår, når temps bruges refleksivt til simple afledte værdier, der ville være tydeligere som navngivne metoder, eller når de akkumuleres på tværs af en lang metode og tvinger læsere til at spore flere samtidigt aktive mellemliggende værdier.
Hvad er refactoring i softwareudvikling?
Refactoring er processen med at omstrukturere eksisterende kode uden at ændre dens observerbare adfærd. Målet er at forbedre kodens interne kvalitet: dens læsbarhed, testbarhed, vedligeholdelsesvenlighed og modularitet. En refactoring tilføjer ikke funktioner og retter ikke fejl; den ændrer kodens struktur, samtidig med at den bevarer det, den gør.
Erstat Temp med Query er én refaktorering i et katalog over flere dusin teknikker beskrevet af Martin Fowler. Den tilhører en familie af teknikker, der beskæftiger sig med metoder, der er blevet for lange eller for komplekse:
| Refactoring-teknik | Hvad det gør |
|---|---|
| Erstat midlertidig med forespørgsel | Udtrækker en midlertidig variabels beregning til en navngiven metode |
| Udtræk metode | Udtrækker en kodeblok til en ny navngivet metode |
| Indbygget temperatur | Erstatter en simpel temp direkte med dens udtryk |
| Opdel midlertidig variabel | Opdeler en temperatur, der genbruges til forskellige formål, i forskellige variabler |
| Erstat løkken med rørledningen | Erstatter en imperativ løkke med en funktionel pipeline (kortlægger, filterer, reducerer) |
| Introducer forklaring af variable | Introducerer en navngiven midlertidig funktion for at tydeliggøre et komplekst udtryk |
Disse teknikker bruges ikke isoleret. Fowler beskriver "Erstat Temp med Query" som et vigtigt trin før "Extract Method": hvis en metode har temp-variabler, bliver det vanskeligt at udtrække en del af den til en ny metode, fordi disse temp-variabler kan bruges både før og efter den udtrukne sektion. Ved først at fjerne de temp-variabler ved at omdanne dem til forespørgsler, baner man vejen for udtrækningen.
Hvad er Erstat Temp med Forespørgsel?
Erstat Temp med Query er en refaktoreringsteknik, der omdanner en lokal midlertidig variabel til et metodekald. I stedet for at beregne en værdi og tildele den til en lokal variabel, udtrækker du beregningen til en privat metode, forespørgslen, som returnerer den beregnede værdi, når den kaldes. Uanset hvor temp blev brugt, erstatter du referencen med et kald til forespørgselsmetoden.
Det kanoniske eksempel fra Fowlers Refactoring :
Før:
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;
}
Forespørgselsmetoden basePrice() er nu en navngiven, selvstændig beregning. Den kan kaldes fra enhver anden metode i klassen, testes uafhængigt, tilskrives i underklasser og forstås uden først at læse den kaldende metode.
Problemet med midlertidige variabler
De fragmenterer logik på tværs af en metode
En midlertidig variabel opdeler en beregning i to adskilte dele: tildelingen (hvor værdien beregnes) og brugen (hvor den læses). I en kort metode er denne opdeling harmløs. I en metode, der er vokset til tredive eller halvtreds linjer, kan tildelingen og brugen være adskilt af mange linjer med anden logik. Læseren skal rulle op for at finde tildelingen, gemme betydningen i arbejdshukommelsen og rulle tilbage til brugen. Hver yderligere midlertidig variabel forværrer dette kognitive overhead.
De blokerer ekstraktmetoden
Det mest betydelige praktiske problem med temps er, at de blokerer andre refaktoreringer. Overvej en metode med en kompleks betinget gren, der ville have gavn af at blive udtrukket i sin egen metode. Hvis grenen bruger en temp, der blev tildelt tidligere i metoden, kræver udtrækningen enten at temp overføres som en parameter, at temp gøres til en instansvariabel eller at dens værdi beregnes igen inde i den udtrukne metode. Ingen af disse muligheder er rene. Hvis man først fjerner temp ved at erstatte den med en forespørgsel, fjernes denne hindring helt.
De inviterer til genbrug og mutation
Midlertidige variabler genbruges undertiden til forskellige formål inden for den samme metode, en praksis som Fowler kalder "midlertidig variabel-tanglet". En variabel med navnet temp or result der gentildeles flere gange, giver ingen semantisk information og vildleder aktivt læserne om, hvad den repræsenterer på et givet tidspunkt. Selv midlertidige værdier med et enkelt formål kan akkumuleres, indtil en metodes omfang er rodet med mellemliggende værdier, som læserne skal spore samtidigt.
Trin for trin: Sådan anvender du Erstat midlertidigt med forespørgsel
Transformationen følger fire trin, der kan anvendes sikkert på ethvert sprog:
Trin 1: Bekræft, at temperaturen er tildelt præcis én gang og aldrig muteres. Hvis temperaturen tildeles igen senere i metoden, skal den først opdeles ved hjælp af Opdel midlertidig variabel.
Trin 2: Udtræk højre side af opgaven til en privat metode. Giv metoden et navn, der beskriver, hvad den beregner, ikke hvordan. basePrice() er bedre end calculateQuantityTimesPrice().
Trin 3: Erstat alle referencer til temp med et kald til den nye metode. De fleste IDE'er kan gøre dette automatisk: højreklik på temp → Refactor → Inline Variable, og udtræk derefter metoden på det indlejrede udtryk.
Trin 4: Slet deklarationen af temp-variablen. Hvis udtrækningen er fuldført, burde temp nu ikke have nogen referencer og kan fjernes.
Java: Fuldt fungerende eksempel
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 et enkelt udtryk, der tydeligt kommunikerer beregningen. Hver udtrukne forespørgsel kan læses, testes og udvides uafhængigt. Bemærk, at discountFactor() opkald basePrice(), dette er korrekt fordi basePrice() er en ren beregning uden bivirkninger, så det at kalde den to gange introducerer ingen risiko.
Python: Erstat Temp med Property
I Python er den naturlige ækvivalent til en forespørgselsmetode en @property, hvilket tillader metoden at blive kaldt uden parenteser og læser identisk med en attribut access:
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
Ved brug af @property midler self.base_price læser identisk med en instansvariabel, hvilket gør den kaldende kode self.base_price * self.discount_factor helt naturlig. Hver egenskab kan testes uafhængigt:
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
Dette niveau af testbarhed er umuligt med den midlertidigt baserede version: de interne beregninger base_price og discount_factor er ikke tilgængelige udefra metoden.
TypeScript: Forespørgselsmetoder og Getters
TypeScript understøtter både metodebaserede forespørgsler og property getters, der matcher de mønstre, der er tilgængelige i henholdsvis Java og Python:
maskinskrift
// 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;
}
}
maskinskrift
// 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;
}
}
God navngivning af forespørgselsmetoder
Forespørgselsmetodens navn udfører det vigtigste arbejde. En dårligt navngivet udtrækning er værre end den midlertidige metode, den erstattede, fordi den skaber en uigennemsigtig indirekte metode: kaldere skal navigere til metodedefinitionen for at forstå, hvad den gør, hvilket modvirker formålet.
Gode navne på forespørgselsmetoder følger disse principper:
Nævn hvad det repræsenterer, ikke hvordan det beregnes. basePrice() kommunikerer forretningskonceptet. getQuantityTimesItemPrice() beskriver beregningen, ikke konceptet. Sondringen er vigtig, når beregningen ændres, konceptnavnet basePrice() forbliver stabil, selvom formlen ændres.
Brug navneordsfraser til værdier. Forespørgselsmetoder returnerer værdier; de er ikke kommandoer. discountFactor(), totalAmount(), isEligible() følg konventionen om at navngive det, der returneres. calculateDiscount(), processAmount() følg den imperative konvention for kommandoer, hvilket er forvirrende for metoder, der udelukkende beregner og returnerer.
Booleske forespørgsler skal læses som spørgsmål. isHighValue(), hasDiscount(), meetsThreshold() kommunikere, at retursvaret er en boolsk værdi, og at den, der ringer, stiller et ja/nej-spørgsmål. bool 変数名 (boolsk variabelnavngivning, en forespørgsel i Search Console-dataene) afspejler præcis denne bekymring: boolske variabler og metoder har brug for navne, der gør deres betydning tydelig på brugstidspunktet.
Hold navnene stabile på tværs af relaterede metoder. If basePrice() bruges af discountFactor(), navngivningskonsistensen fortæller læserne, at discountFactor afhænger basePriceInkonsekvent navngivning bryder denne implicitte dokumentation.
Hvornår skal man anvende Erstat midlertidigt med forespørgsel
Anvend denne refaktorering når:
- Temperaturen tildeles præcis én gang og tildeles aldrig igen
- Beregningen er et rent udtryk: den læser fra felter eller parametre, men ændrer ikke den eksterne tilstand, kalder ikke netværkstjenester eller afhænger ikke af tid eller tilfældighed.
- Beregningen er kompleks nok til, at det at navngive den ville forbedre læsbarheden, eller simpel nok til, at temperaturen bare er rod.
- Du er ved at anvende Udtræksmetoden på en blok, der bruger den midlertidige metode
Det mest almindelige ideelle scenarie er en afledt værdi: en pris, en total, en rabat, en formateret streng, en betinget klassificering. Disse er værdier, der er udelukkende afledt af objektets felter, uden bivirkninger, og som naturligt hører hjemme som egenskaber for objektet snarere end som mellemliggende beregninger i en metode.
Hvornår det ikke skal anvendes Erstat midlertidig med forespørgsel
Ydelsesfølsomme operationer. Hvis beregningen er dyr, indebærer en databaseforespørgsel, et netværkskald, en O(n²)-løkke, to kald af forespørgselsmetoden dobbelte omkostninger. Temp-funktionen eksisterer netop for at undgå dette. I disse tilfælde skal du enten lade temp-funktionen være på plads eller huske forespørgselsmetoden (cache resultatet efter det første kald):
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
Negative operationer. Hvis den midlertidige funktion indeholder resultatet af en operation, der kun skal køres én gang (generering af et unikt ID, logføring, skrivning til en fil), vil det at omdanne den til en forespørgsel køre operationen ved hvert kald. Dette ændrer programmets funktionsmåde, ikke kun dets struktur. Anvend ikke denne refaktorering på midlertidige funktioner med negative virkninger.
Temperamenter, der akkumuleres på tværs af loop-iterationer. En temperatur, der er akkumulatoren i en for løkke, total += item.price, er ikke en kandidat til Erstat midlertidig med forespørgsel. Det er ikke en afledt værdi; det er en tilstand, der opbygges på tværs af iterationer. Overvej i stedet at Erstat løkke med pipeline, hvis løkken er problemet.
Relaterede refactoringteknikker
Erstat midlertidig med forespørgsel tilhører en familie af teknikker, der tilsammen eliminerer unødvendig kompleksitet i metoder. Forståelse af familien hjælper udviklere med at vælge den rigtige teknik til det aktuelle problem:
Udtræksmetoden er den mest almindelige ledsager. Erstat Temp med Query gør ofte Udtræksmetoden mulig ved at rydde variabler, der ellers ville kræve akavet parameteroverførsel mellem den udtrukne del og resten af metoden.
Inline Temp er det modsatte af at introducere en temp: den erstatter en temp med dens udtryk direkte i koden. Brug Inline Temp, når temp ikke tilføjer klarhed, og dens udtryk allerede er læsbart.
Opdel midlertidig variabel anvendes, når en enkelt vikar genbruges til flere formål i den samme metode. Opdel den i separate variabler med navne, der afspejler hvert formål, og anvend derefter Erstat midlertidig med forespørgsel på en af de resulterende engangsvikarer.
Introduktion af forklaring af variabel er den modsatte retning: hvis et komplekst udtryk er svært at læse, kan introduktion af en temp med et beskrivende navn forbedre klarheden. Denne teknik og Erstat Temp med Query er i modstrid, og udvikleren skal vurdere, hvilken retning der forbedrer den specifikke kode.
Erstat løkken med rørledningen adresserer et almindeligt mønster, hvor en løkke med en temp-akkumulator kan erstattes af en kædet pipeline-operation (map, filter, reduce), som er mere deklarativ og lettere at læse.
Hvordan SMART TS XL Understøtter refactoring i stor skala
"Erstat midlertidigt med forespørgsel" er en lokal refaktorering: den transformerer én variabel i én metode. I en kodebase af enhver betydelig størrelse er det mere nyttige spørgsmål ikke "hvordan anvender jeg denne refaktorering?", men "hvor i hele kodebasen skal jeg anvende den, og hvad vil blive påvirket, når jeg gør det?"
SMART TS XL leverer den strukturelle analyse på tværs af kodebasen, der gør besvarelsen af dette spørgsmål systematisk. Den identificerer, hvor den samme beregning udføres som en midlertidig variabel på flere steder, det mønster, som Erstat midlertidigt med forespørgsel er designet til at konsolidere til en enkelt navngiven forespørgselsmetode. Den sporer, hvordan en refaktoreret forespørgselsmetode bruges, når den er udtrukket, hvilket gør omfanget af refaktoreringen synlig, før den udføres. Og den fungerer på tværs af sprog: for virksomhedssystemer, hvor COBOL-programmer, Java-tjenester og Python-pipelines alle opererer på de samme data, statisk kodeanalyse og konsekvensanalyse Identificer hvor den samme logiske beregning optræder i forskellige former på tværs af forskellige sprog, den dybere form af problemet som Erstat midlertidig med forespørgsel adresserer på enkeltsproget niveau.
For teams, der arbejder på arvemodernisering, SMART TS XL's visualisering af afhængigheder gør det muligt at se, hvor refaktorerede komponenter bruges, før de ændres, hvilket sikrer, at udtrækning af en beregning i en forespørgselsmetode ikke ødelægger kaldere, der forventede den oprindelige struktur.
Midlertidige variabler og selvdokumenterende kode
Beslutningen om at erstatte en midlertidig forespørgsel med en forespørgsel er i sidste ende en beslutning om, hvad koden skal kommunikere. Midlertidige variabler kommunikerer implementering: dette er den beregning, jeg udførte for at få denne værdi. Forespørgselsmetoder kommunikerer domæne: dette er, hvad denne værdi betyder. I en Order-klasse, basePrice() fortæller læseren, at dette koncept findes i domænet. double x = quantity * itemPrice fortæller læseren om en aritmetisk operation.
Efterhånden som kode udvikler sig, har domænekoncepter brug for stabile hjem. En beregning, der er indlejret i en midlertidig kode, kan ændres, duplikeres i flere metoder eller misforstås af den næste udvikler, der læser den. En navngiven forespørgselsmetode kan findes, testes, dokumenteres og udvikles bevidst. Denne stabilitet, på tværs af alle de steder, hvor beregningen er nødvendig, og på tværs af alle de udviklere, der vil arbejde med den, er det, der gør Erstat midlertidig kode med forespørgsel til mere end en syntaksændring. Det er en beslutning om, hvordan kodebasen kommunikerer det problem, den løser.