Category
Simplifying Conditional Expressions
Techniques that flatten, name, merge and replace conditionals so branching logic reads as a list of plain decisions.
What these techniques solve
Conditionals grow by accretion. Each bug fix adds a clause, each feature adds a branch, and the result is nested if blocks with duplicated tails, control flags and null checks scattered across callers. These techniques make each decision explicit, remove repetition between branches, and move type-based branching into the types themselves.
A reader should see the normal path first and each exception as one named, early decision.
Members
- Decompose Conditional: extract the condition and each branch body into well-named methods.
- Consolidate Conditional Expression: merge several checks that lead to the same result into one named condition.
- Consolidate Duplicate Conditional Fragments: move code that runs in every branch outside the conditional.
- Remove Control Flag: replace a boolean that steers a loop with
break,continueorreturn. - Replace Nested Conditional with Guard Clauses: handle special cases with early returns so the main path isn't nested.
- Replace Conditional with Polymorphism: move each branch of a type-based conditional into a method on the matching subclass or strategy.
- Introduce Null Object: return an object with safe default behavior instead of
null, so callers stop checking. - Introduce Assertion: make an assumption the code already relies on explicit with an assertion.
How to choose within this category
- The condition itself is hard to read: Decompose Conditional. Several conditions with the same outcome: Consolidate Conditional Expression first, then name the result.
- Branches end or start with the same lines: Consolidate Duplicate Conditional Fragments.
- Deep nesting where outer branches only check for special cases: Replace Nested Conditional with Guard Clauses.
- A
doneorfoundflag controls a loop: Remove Control Flag. Often a standard library call (find,some,any) removes the loop entirely. - The same
switchon a type appears in several places: Replace Conditional with Polymorphism. For a singleswitchover a closed union, exhaustive pattern matching (TypeScriptneverchecks, Rustmatch, Java sealedswitch) is often clearer than a class per case. if (x == null)checks repeat for the same default behavior: Introduce Null Object. Where optional chaining (?.),??, orOptionalalready reads well, prefer those.- Code silently assumes something is true: Introduce Assertion. Don't use assertions to validate user input; that is error handling, not an invariant.
See also
- Composing MethodsTechniques that split, merge and reshape method bodies so each method does one readable thing and local variables stop blocking change.
- Organizing DataTechniques that turn raw fields, magic values, arrays and type codes into objects and types that carry meaning and protect their own invariants.
- Switch StatementsThe same switch or if-chain over a type code is repeated across the codebase, so adding one case means editing every copy.
- Long MethodA function that does several jobs in a row and has grown too long to read, name or test as one unit.
Original writing and examples. Topic coverage follows Refactoring.Guru, which explains this topic with its own illustrations.