Transform scattered inputs into one clear object
TL;DR: Wrap messy parameters into a single meaningful entity.
Problems Addressed π
- Parameter overload
- Order confusion
- Anemic Models
- Low readability
- Hard extension
- DTOs
- Weak semantics
- Repeated validations
- Accidental argument swapping
- Primitive Obsession
- Data Clumps
- Weak type safety
- Missing Small Objects
- Missing Intervals
Related Code Smells π¨
https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-xviii
https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-ii-o96s3wl4
https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-xxxvi
https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-xxxix
https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-x-i7r34uj
https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-xxix
https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-viii-8mn3352
https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-i-xqz3evd
https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-xxxviii
https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-xix
https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-xxv
https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-iv-7sc3w8n
Steps π£
- Identify multiple parameters of the same type
- Create a meaningful entity to group them
- Add missing validation rules to fail fast
- Replace function signatures with the new entity
- Adjust all callers to pass the entity
- Add context-specific names to improve clarity
Sample Code π»
Before π¨
function findHolidays(
maxPrice: Currency,
startDate: Date,
endDate: Date,
minPrice: Currency) {
// Notice that maxPrice and minPrice are swapped by mistake
// Also, dates are mixed
}
After π
// 2. Create a meaningful entity to group them
class PriceRange {
constructor(public min: Currency, public max: Currency) {
if (min > max) {
throw new Error(
`Invalid price range: min (${min}) `+
`cannot be greater than max (${max})`
);
}
if (min < 0) {
throw new Error(
`Invalid price range: min (${min}) cannot be negative`);
}
}
}
class Interval {
// 3. Add missing validation rules to fail-fast
constructor(public start: Date, public end: Date) {
if (start > end) {
throw new Error(
`Invalid date range: start (${start.toISOString()}) ` +
`cannot be after end (${end.toISOString()})`
);
}
}
}
class HolidaySearchCriteria {
constructor(
public priceRange: PriceRange,
public dateRange: Interval
) {}
}
function findHolidays(criteria: HolidaySearchCriteria): Holiday[] {
// 1. Identify multiple parameters of the same type
// No need to call validate() - already validated in constructors
// 4. Replace function signatures with the new entity
const { priceRange, dateRange } = criteria;
// 5. Adjust all callers to pass the entity
// 6. Add context-specific names to improve clarity
return database.query({
price: { $gte: priceRange.min, $lte: priceRange.max },
startDate: { $gte: dateRange.start },
endDate: { $lte: dateRange.end }
});
}
try {
const criteria = new HolidaySearchCriteria(
new PriceRange(500, 1000), // β
Valid
new Inteval(
new Date('2025-06-01'),
new Date('2025-06-15')
)
);
findHolidays(criteria);
// β This will throw immediately
// Great for UI and API validation
new PriceRange(1000, 500);
} catch (error) {
console.error(error.message);
}
Type π
- Semi-Automatic
Safety π‘οΈ
Many IDEs support this pattern.
Why is the Code Better? β¨
You avoid order confusion and increase readability.
You make functions easy to extend with new parameters.
You bring semantic meaning to the input data.
You eliminate the risk of passing arguments in the wrong order since the object properties have explicit names.
You make function calls self-documenting because each value clearly indicates its purpose.
You simplify adding new optional parameters without breaking existing code.
You enable better IDE support with autocomplete showing parameter names.
You create opportunities to reuse the parameter object type across related functions.
You fail fast, asserting on the relations among parameters.
How Does it Improve the Bijection? πΊοΈ
You move closer to a one-to-one map between the business concept of a "search request" and your code model.
You stop treating the data as loose numbers and give them an explicit identity that matches the domain.
In the real world, you describe searches using named criteria rather than ordered lists.
When you ask someone to "search for products with a minimum price of 50 and a maximum price of 100," you use named concepts.
This refactoring mirrors that natural language structure in your code.
The SearchCriteria becomes a first-class concept that maps directly to how searching works in the real world.
Refactor with AI π€
Ask AI to scan your codebase for functions that use two or more parameters of the same type.
Instruct it to propose an entity name, generate the type or class, and rewrite both the function and its callers to use the new entity.
Suggested Prompt: 1. Identify multiple parameters of the same type 2. Create a meaningful entity to group them 3. Add missing validation rules to fail fast 4. Replace function signatures with the new entity 5. Adjust all callers to pass the entity 6. Add context-specific names to improve clarity
Without Proper Instructions |
With Specific Instructions |
---|---|
Tags π·οΈ
- Primitive Obsession
Level π
- [x]Intermediate
Related Refactorings π
https://refactoring.guru/es/introduce-parameter-object?embedable=true
https://hackernoon.com/refactoring-013-eliminating-repeated-code-with-dry-principles?embedable=true
https://hackernoon.com/refactoring-019-how-to-reify-email-addresses?embedable=true
Also known as
https://refactoring.guru/es/introduce-parameter-object?embedable=true
Credits π
Image by Gerd Altmann on Pixabay
This article is part of the Refactoring Series.
https://maximilianocontieri.com/how-to-improve-your-code-with-easy-refactorings?embedable=true