Introduce Parameter Object and Preserve Whole Object
What This Concept Is
Two refactor moves that shrink parameter lists by respecting the shape of data.
Introduce Parameter Object: when several parameters frequently travel together (a data clump), wrap them in a small class and pass that instead. Typical clump: (startDate, endDate), (x, y), (low, high).
Preserve Whole Object: when the caller pulls several values out of a record and passes the parts, pass the whole record instead. The function body derives what it needs.
Connection to primary concepts (Cluster 3): these are often applied immediately after Encapsulate Record or as preparation for Replace Primitive with Object. The parameter object is the new type's first class.
Why It Matters Here
Long parameter lists are a smell. Each extra parameter is a coupling to the call site's data shape. More importantly, parameters that travel together in three functions will travel together in four, five, ten -- at which point every signature change ripples across all of them.
Preserve Whole Object avoids the opposite failure: pulling aRoom.daysTempRange.low and .high out just to pass them. A day later, the function needs the midpoint too, and suddenly every caller changes.
Concrete Example
Introduce Parameter Object. Before -- a numeric-range clump:
function amountInvoiced(customer, startDate, endDate) { /* ... */ }
function amountReceived(customer, startDate, endDate) { /* ... */ }
function amountOverdue (customer, startDate, endDate) { /* ... */ }
After:
class DateRange {
constructor(start, end) { this._start = start; this._end = end; }
get start() { return this._start; }
get end() { return this._end; }
includes(date) { return date >= this._start && date <= this._end; }
}
function amountInvoiced(customer, range) { /* ... */ }
function amountReceived(customer, range) { /* ... */ }
function amountOverdue (customer, range) { /* ... */ }
The includes method is the first piece of behavior that now has a natural home -- behavior that otherwise would be duplicated inside each of the three call sites.
Preserve Whole Object. Before:
const low = aRoom.daysTempRange.low;
const high = aRoom.daysTempRange.high;
if (!aPlan.withinRange(low, high)) alerts.push("out of range");
After:
if (!aPlan.withinRange(aRoom.daysTempRange)) alerts.push("out of range");
Now withinRange decides how to read low and high. Tomorrow it can also look at median without the caller being touched.
Common Confusion / Misconception
"Parameter objects are always better." Not when the caller does not have the whole object. If three parameters come from three unrelated places, lumping them into a parameter object creates a ceremonial wrapper with no real meaning.
Preserve Whole Object has a limit: it creates a dependency from the called function to the whole class. If the function lives in a module that should not know about Room, do not preserve the whole -- unpack at a boundary.
How To Use It
Mechanics for Introduce Parameter Object:
- Create the class. Start with just the fields.
- Change Function Declaration (parallel change): add a new parameter of the new type alongside the old ones.
- Callers start passing the object; the function derives values from it.
- Remove the original parameters.
- Migrate related behavior onto the parameter class as it appears.
Check Yourself
- Name two signals that say "do not introduce a parameter object yet."
- When does Preserve Whole Object couple the called function to the wrong layer?
- How does Introduce Parameter Object set up a later Replace Primitive with Object?
Mini Drill or Application
Find a function in your code with 4+ parameters. Identify any subset that travels together in at least one other function. Introduce Parameter Object. Migrate two callers. Look for a method that would now naturally land on the new class and move it there.
Video and Lecture References
- Primary lecture: Refactoring.guru -- Introduce Parameter Object (animated)
- Visual supplement: Refactoring.guru -- Preserve Whole Object (animated)
Article References
- Refactoring.com: Introduce Parameter Object
- Refactoring.com: Preserve Whole Object
- Fowler: DataClump - the smell
External Exercises
- Grep your code for functions with 4+ parameters. For each, decide: clump? Leave? Sketch the parameter object.
- Refactoring.guru: Data Clumps smell
Depth Path
- Read This Only If Stuck - Fowler chunks 045 (Rename Variable and Introduce Parameter Object), 074 (Preserve Whole Object)
- Optional deep dive: chunk 073 (Parameterize Function) for the related "same shape, different literals" case
Source Backbone
Refactoring is the canonical book backbone for this module. Use these sources after attempting the refactor and tests yourself.
- Refactoring (Fowler) - primary source for refactoring discipline and named moves.
- Clean Code - support for readability and small-function judgment.
- Good Code, Bad Code - support for maintainability tradeoffs.