Skip to main content

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):

  1. Ensure no side effects in any condition (Separate Query from Modifier if needed).
  2. Combine two conditions with || (sequence of guards returning same value) or && (nested ifs).
  3. Repeat until one condition remains.
  4. Extract the combined condition into a named function.

Check Yourself

  1. Why is Consolidate Conditional Expression dangerous if a condition has a side effect?
  2. Which of these two moves is the precondition for Replace Conditional with Polymorphism -- and which helps you decide not to go polymorphic?
  3. Give an example where leaving three separate if guards 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

Article References

External Exercises

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.