Refactoring techniqueComposing Methods
Replace Temp with Query
Move the expression behind a temporary variable into its own method so every method in the class can ask for the value.
Problem
A method computes a value into a local variable and uses it later. Other methods in the class compute the same value their own way, and the local makes the long method hard to split because every fragment depends on it.
Solution
Extract the expression into a method (a query) with no side effects, and replace the variable with calls to it. The value is now available anywhere in the class and does not tie fragments together.
Before
class Shipment {
constructor(private parcels: Parcel[], private zone: Zone) {}
label(): string {
const billableKg = Math.max(
this.parcels.reduce((s, p) => s + p.weightKg, 0),
this.parcels.reduce((s, p) => s + p.volumeCm3, 0) / 5000,
);
const fee = billableKg * this.zone.ratePerKg;
return `${this.zone.code} ${billableKg.toFixed(1)}kg ${fee.toFixed(2)}`;
}
needsFreightCarrier(): boolean {
const actualKg = this.parcels.reduce((s, p) => s + p.weightKg, 0);
const volumetricKg = this.parcels.reduce((s, p) => s + p.volumeCm3, 0) / 5000;
return Math.max(actualKg, volumetricKg) > 70;
}
}After
class Shipment {
constructor(private parcels: Parcel[], private zone: Zone) {}
label(): string {
return `${this.zone.code} ${this.billableKg().toFixed(1)}kg ${this.fee().toFixed(2)}`;
}
needsFreightCarrier(): boolean {
return this.billableKg() > 70;
}
private billableKg(): number {
const actualKg = this.parcels.reduce((s, p) => s + p.weightKg, 0);
const volumetricKg = this.parcels.reduce((s, p) => s + p.volumeCm3, 0) / 5000;
return Math.max(actualKg, volumetricKg);
}
private fee(): number {
return this.billableKg() * this.zone.ratePerKg;
}
}Why
- The billable-weight rule lives in one place. A change to the volumetric divisor touches one line.
labellost its locals, so further extraction is trivial.- Queries document themselves in IDE outlines and can be tested.
Steps
- Check the variable is assigned once. If it is assigned in several places, apply Split Temporary Variable first.
- Extract the right-hand side into a private method. Make sure the method does not modify state.
- Run the tests.
- Replace each read of the variable with a call to the new method, or apply Inline Temp. Run the tests.
- Replace duplicate computations elsewhere in the class with the query.
Watch out
- Repeated calls repeat the computation. That is fine for cheap expressions. For costly ones (database reads, large reductions inside a loop), keep the variable or memoize, and measure first.
- Don't apply when the variable captures a snapshot that must not change, such as a timestamp taken at the start of a transaction.
- The query must be side-effect free. If the expression mutates anything, separate the mutation first.
- The opposite move is to keep or reintroduce a local with Extract Variable when the value is only meaningful in one method.
Related
- Fixes Long Method and Duplicate Code.
- Usually the step before Extract Method.
- When locals are too entangled for queries, use Replace Method with Method Object.
Agent checklist
- The variable was assigned once and the query has no side effects.
- The value cannot change between calls within the original method, or a change is acceptable.
- Performance of repeated calls was considered for non-trivial expressions.
- Duplicate computations elsewhere now use the query.
- Tests pass.
Treats these smells
See also
- Extract MethodMove a coherent fragment of a long function into its own well-named function and call it from the original place.
- Inline TempReplace a temporary variable that holds a simple expression with the expression itself when the name adds nothing.
- Extract VariableName a hard-to-read expression or sub-expression by assigning it to a well-named local variable.
- Replace Method with Method ObjectTurn a long method with tangled local variables into its own class, so the locals become fields and the body can be split freely.
Original writing and examples. Topic coverage follows Refactoring.Guru, which explains this topic with its own illustrations.