Decompose and Consolidate Conditional
What This Concept Is
Two complementary moves for single-site conditional logic. They are the go-to before anyone reaches for polymorphism.
Decompose Conditional: extract the condition, the then-branch, and the else-branch each into named functions. The code now reads if (reason()) thenAction(); else elseAction();. Conditional length goes down; intent goes up.
Consolidate Conditional Expression: when several separate conditional checks all return the same answer, combine them into one condition (with && / ||), and often extract that combined condition into a named function.
Connection to primary concept (Replace Conditional with Polymorphism): these moves are the single-function version of conditional cleanup. Polymorphism is the cross-function version. Decompose / Consolidate first; polymorphism only when recurrence shows up.
Why It Matters Here
A tangled multi-line if condition or a tall stack of guard-style ifs hides the why behind the what. The goal of these two refactors is to name the why.
They are also the best defense against the mistake of over-applying polymorphism. Most conditionals are not recurring and do not need a hierarchy; they need extraction and a better name.
Concrete Example
Decompose Conditional:
// Before
if (!aDate.isBefore(plan.summerStart) && !aDate.isAfter(plan.summerEnd))
charge = quantity * plan.summerRate;
else
charge = quantity * plan.regularRate + plan.regularServiceCharge;
// After
if (summer(aDate, plan)) charge = summerCharge(quantity, plan);
else charge = regularCharge(quantity, plan);
function summer(aDate, plan) {
return !aDate.isBefore(plan.summerStart) && !aDate.isAfter(plan.summerEnd);
}
function summerCharge(q, plan) { return q * plan.summerRate; }
function regularCharge(q, plan) { return q * plan.regularRate + plan.regularServiceCharge; }
The conditional now reads like a sentence: "if summer, charge summer; else charge regular."
Consolidate Conditional Expression:
// Before
function disabilityAmount(anEmployee) {
if (anEmployee.seniority < 2) return 0;
if (anEmployee.monthsDisabled > 12) return 0;
if (anEmployee.isPartTime) return 0;
// compute the disability amount
}
// After
function disabilityAmount(anEmployee) {
if (isNotEligibleForDisability(anEmployee)) return 0;
// compute the disability amount
}
function isNotEligibleForDisability(e) {
return e.seniority < 2
|| e.monthsDisabled > 12
|| e.isPartTime;
}
Three "same-result" guards have collapsed into one named question.
Common Confusion / Misconception
Consolidating checks that have side effects. If any of the conditions call a function that mutates state, combining them changes short-circuit semantics and order of effects. Separate Query from Modifier first.
Don't consolidate when checks are genuinely independent. If each of the three guards reflects a different business rule that might evolve separately, leaving them as a sequence is clearer, even if repetitive.
Decomposing trivially simple conditions. if (x > 0) does not need a isPositive() helper. Decompose Conditional pays when the condition is itself multi-part or reads ambiguously.
How To Use It
Mechanics (Consolidate):
- Ensure no side effects in any condition (Separate Query from Modifier if needed).
- Combine two conditions with
||(sequence of guards returning same value) or&&(nested ifs). - Repeat until one condition remains.
- Extract the combined condition into a named function.
Check Yourself
- Why is Consolidate Conditional Expression dangerous if a condition has a side effect?
- Which of these two moves is the precondition for Replace Conditional with Polymorphism -- and which helps you decide not to go polymorphic?
- Give an example where leaving three separate
ifguards is preferable to consolidating them.
Mini Drill or Application
Find a function in your code with an if whose condition is at least two logical operators deep. Decompose it: name the condition, name each branch. Reread. If the function now reads like English, you are done. If not, the names are wrong -- try again or extract further.
Video and Lecture References
- Primary lecture: Refactoring.guru -- Decompose Conditional (animated)
- Visual supplement: Refactoring.guru -- Consolidate Conditional Expression (animated)
Article References
- Refactoring.com: Decompose Conditional
- Refactoring.com: Consolidate Conditional Expression
- Refactoring.com: Replace Nested Conditional with Guard Clauses - the third sibling move
External Exercises
- Find a function with a nested
ifthree levels deep. Apply Replace Nested Conditional with Guard Clauses first, then Decompose Conditional on what remains. - Refactoring.guru: Simplifying Conditional Expressions
Depth Path
- Read This Only If Stuck - Fowler chunks 066 (Decompose Conditional), 067 (Consolidate Conditional Expression + Guard Clauses)
- Optional deep dive: chunk 070-071 (Introduce Special Case) for null-object handling as a conditional alternative
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.