Refactoring & Code Quality
Composing Methods & Conditionals
A family of refactorings targets conditional logic specifically: flattening nested ifs with guard clauses, decomposing a tangled condition into named pieces, and — when a type-code switch keeps growing — replacing it with polymorphic dispatch.
- Guard clauses: handle the exceptional/early-exit cases first and return immediately, so the main logic isn't nested inside an
if - Decompose Conditional: extract the condition, the then-branch, and the else-branch each into a well-named method
- Replace Conditional with Polymorphism: turn a type-code switch into one method per subtype, each overriding a common method
- The "one level of indentation" heuristic — deeply nested conditionals are themselves a smell (Long Method's conditional cousin)
- Polymorphism wins when the same switch is duplicated at several call sites and the set of types keeps growing; a single, stable, local
ifis often clearer left alone
// Before: the "normal" case is buried three levels deep.
double getPayAmount(Employee e) {
double result;
if (e.isSeparated()) {
result = 0;
} else {
if (e.isRetired()) {
result = pension(e);
} else {
result = normalPay(e);
}
}
return result;
}
// After: guard clauses handle exceptions first; the main path is flat.
double getPayAmount(Employee e) {
if (e.isSeparated()) return 0;
if (e.isRetired()) return pension(e);
return normalPay(e);
}// Before: repeated at every call site that needs a rate.
double rateFor(Employee e) {
switch (e.getType()) {
case ENGINEER: return 1.0;
case MANAGER: return 1.5;
case DIRECTOR: return 2.0;
default: throw new IllegalArgumentException();
}
}
// After: each subtype knows its own rate; no switch anywhere.
abstract class Employee { abstract double rate(); }
class Engineer extends Employee { double rate() { return 1.0; } }
class Manager extends Employee { double rate() { return 1.5; } }
class Director extends Employee { double rate() { return 2.0; } }| Signal | Prefer |
|---|---|
| One call site, stable set of cases, unlikely to grow | Plain conditional — polymorphism would be needless indirection |
| Same switch duplicated at several call sites | Polymorphism — one dispatch point instead of N copies to keep in sync |
| New cases get added regularly (OCP pressure, see Solid Principles) | Polymorphism — a new case is a new subclass, no existing code edited |