| name | refactoring-simplifying-conditionals |
| description | Techniques for simplifying conditional logic: Decompose Conditional, Consolidate Conditional Expression, Consolidate Duplicate Conditional Fragments, Remove Control Flag, Replace Nested Conditions with Guard Clauses, Replace Conditional with Polymorphism. All with Python before/after examples. |
Simplifying Conditional Expressions
Conditional logic is often the hardest code to follow. These refactorings make conditionals clearer, flatter, and more expressive.
When to Use This Skill
Triggers:
- Nested if/else blocks are hard to follow
- Conditional expressions are long and complex
- The same code appears in multiple branches of a conditional
- A boolean flag controls flow through a loop or method
- A growing chain of if-elif-else or switch/match statements
- Conditional logic varies by type but the types don't use polymorphism
Don't Triggers:
- The problem is that a method is too long in general (use
refactoring-composing-methods)
- The problem is about which class the logic belongs in (use
refactoring-moving-features)
- You're trying to identify what's wrong at a high level (use
refactoring-code-smells)
Decompose Conditional
Problem: You have a complicated conditional (if-then-else) expression.
Solution: Extract the condition, the then-branch, and the else-branch into their own methods.
def calculate_charge(self, quantity, date):
if date.before(SUMMER_START) or date.after(SUMMER_END):
return quantity * self._winter_rate + self._winter_service_charge
else:
return quantity * self._summer_rate
def calculate_charge(self, quantity, date):
if self._is_winter(date):
return self._winter_charge(quantity)
else:
return self._summer_charge(quantity)
def _is_winter(self, date):
return date.before(SUMMER_START) or date.after(SUMMER_END)
def _winter_charge(self, quantity):
return quantity * self._winter_rate + self._winter_service_charge
def _summer_charge(self, quantity):
return quantity * self._summer_rate
Steps:
- Extract the condition into a method named for what it checks.
- Extract the then-branch into a method named for what it does.
- Extract the else-branch into a method named for what it does.
- Replace the conditional with calls to these methods.
Consolidate Conditional Expression
Problem: You have a sequence of conditional tests that all result in the same action.
Solution: Combine them into a single conditional with a logical operator.
def calculate_shipping(self, order):
if order.is_expired:
return 0
if order.is_digital:
return 0
if order.is_free_shipping:
return 0
return order.weight * SHIPPING_RATE
def calculate_shipping(self, order):
if order.is_expired or order.is_digital or order.is_free_shipping:
return 0
return order.weight * SHIPPING_RATE
Reverse case: If conditions are combined but shouldn't be, extract them into separate methods for clarity.
def get_discount(self, customer):
if customer.is_vip or customer.referral_count > 5:
return 0.15
return 0.0
def get_discount(self, customer):
if self._has_vip_discount(customer):
return 0.15
if self._has_referral_discount(customer):
return 0.15
return 0.0
def _has_vip_discount(self, customer):
return customer.is_vip
def _has_referral_discount(self, customer):
return customer.referral_count > 5
Steps:
- Verify that the conditions don't have side effects.
- Combine the conditions using
and or or.
- Test.
Consolidate Duplicate Conditional Fragments
Problem: The same code appears in all branches of a conditional expression.
Solution: Move the duplicated code outside the conditional.
def calculate_total(self, customer, order):
if customer.is_vip:
discount = order.total * 0.1
order.total -= discount
self._send_receipt(order)
else:
discount = order.total * 0.05
order.total -= discount
self._send_receipt(order)
def calculate_total(self, customer, order):
if customer.is_vip:
discount = order.total * 0.1
else:
discount = order.total * 0.05
order.total -= discount
self._send_receipt(order)
Also works for code at the beginning of branches:
def process(self, data):
if data.is_valid:
sanitized = self._sanitize(data)
self._transform(sanitized)
else:
sanitized = self._sanitize(data)
self._log_error(sanitized)
def process(self, data):
sanitized = self._sanitize(data)
if data.is_valid:
self._transform(sanitized)
else:
self._log_error(sanitized)
Steps:
- Identify code that is identical in all branches.
- Move it before or after the conditional.
- Test.
Remove Control Flag
Problem: You have a variable that acts as a control flag for a series of boolean expressions.
Solution: Use break, return, or exception handling instead.
def find_person(self, people):
found = False
result = None
for person in people:
if not found:
if person.name == "Don":
result = person
found = True
elif person.age > 25:
result = person
found = True
return result
def find_person(self, people):
for person in people:
if person.name == "Don":
return person
if person.age > 25:
return person
return None
Steps:
- Find the control flag variable.
- Replace assignments to the flag with
break or return.
- Remove the flag variable and its checks.
Replace Nested Conditions with Guard Clauses
Problem: A method has conditional behavior where the normal path is nested inside exception cases.
Solution: Use guard clauses to handle exceptional cases first and return early.
def calculate_pay(self, employee):
if employee.is_active:
if employee.is_contractor:
return employee.hourly_rate * employee.hours_workd * 0.9
else:
if employee.is_manager:
return employee.salary + employee.bonus
else:
return employee.salary
else:
return 0
def calculate_pay(self, employee):
if not employee.is_active:
return 0
if employee.is_contractor:
return employee.hourly_rate * employee.hours_worked * 0.9
if employee.is_manager:
return employee.salary + employee.bonus
return employee.salary
Steps:
- Identify the "special case" conditions — things that aren't the main flow.
- Replace each special case with a guard clause: check the condition and return early.
- After all guards, the remaining code is the normal flow.
When to use: When the main flow of a method is obscured by exceptional cases. If all branches are equally likely, nesting may be fine.
Replace Conditional with Polymorphism
Problem: You have a conditional that chooses different behavior depending on the type of an object.
Solution: Move each leg of the conditional into an overriding method in a subclass.
class Employee:
def __init__(self, emp_type, salary):
self.emp_type = emp_type
self.salary = salary
def calculate_bonus(self):
if self.emp_type == "engineer":
return self.salary * 0.1
elif self.emp_type == "manager":
return self.salary * 0.2
elif self.emp_type == "sales":
return self.salary * 0.15
else:
raise ValueError(f"Unknown type: {self.emp_type}")
def get_vacation_days(self):
if self.emp_type == "engineer":
return 15
elif self.emp_type == "manager":
return 20
elif self.emp_type == "sales":
return 12
else:
raise ValueError(f"Unknown type: {self.emp_type}")
from abc import ABC, abstractmethod
class Employee(ABC):
def __init__(self, salary):
self.salary = salary
@abstractmethod
def calculate_bonus(self) -> float: ...
@abstractmethod
def get_vacation_days(self) -> int: ...
class Engineer(Employee):
def calculate_bonus(self) -> float:
return self.salary * 0.1
def get_vacation_days(self) -> int:
return 15
class Manager(Employee):
def calculate_bonus(self) -> float:
return self.salary * 0.2
def get_vacation_days(self) -> int:
return 20
class Sales(Employee):
def calculate_bonus(self) -> float:
return self.salary * 0.15
def get_vacation_days(self) -> int:
return 12
Steps:
- Create a base class with abstract methods for each conditional branch.
- Create a subclass for each type. Move each leg of the conditional into the appropriate subclass.
- Replace the conditional with polymorphic calls.
- Remove the type code field from the base class.
When to use: When the same type-based conditional appears in multiple places, and especially when adding a new type requires touching every conditional. If there's only one conditional and it's unlikely to grow, a simple if/else may be clearer.
Summary Table
| Refactoring | Problem | Solution |
|---|
| Decompose Conditional | Complex conditional expression | Extract condition, then, and else into named methods |
| Consolidate Conditional Expression | Multiple conditions with same result | Combine with and/or |
| Consolidate Duplicate Conditional Fragments | Same code in all branches | Move common code outside the conditional |
| Remove Control Flag | Boolean flag controlling flow | Use break/return instead |
| Replace Nested Conditions with Guard Clauses | Normal path buried in nesting | Handle exceptions first, return early |
| Replace Conditional with Polymorphism | Switch on type | Subclasses override methods |
Source: Martin Fowler, "Refactoring: Improving the Design of Existing Code" (2nd Edition)