Eliminating Overused if…else Statements: Refactoring Techniques and Design Patterns
This article examines why excessive if…else statements harm code readability and maintainability, and presents a range of refactoring approaches—including table‑driven mapping, chain‑of‑responsibility, annotation‑driven, event‑driven, state machines, Optional, Assert, and polymorphism—to replace or simplify them, while also addressing deep nesting and complex conditional expressions.
Preface
If…else is a fundamental construct in all high‑level languages, but overusing it degrades readability, maintainability, and ultimately the whole software system. This article explores how to "kill" unnecessary if…else and keep code clean.
Problem One: Too Many if…else
Symptoms
Methods with many if…else branches (often 5, 10, 20 or more) usually violate the Single‑Responsibility and Open‑Closed principles, making the code hard to extend.
How to Solve
The following techniques can reduce the number of branches:
Table‑driven
Chain of Responsibility
Annotation‑driven
Event‑driven
Finite State Machine
Optional
Assert
Polymorphism
Method One: Table‑driven
Introduction
When the logical pattern is fixed, map inputs to handling functions via a lookup table.
Applicable Scenario
Fixed logical patterns.
Implementation and Example
1 if (condition1) {
2
3 } else if (condition2) {
4
5 } else if (condition3) {
6
7 } else if (condition4) {
8
9 } else {
10
11 }Refactored version using a map:
1 Map<?, Function<?>> actionMappings = new HashMap<>(); // demo generic types
2
3 // initialization
4 actionMappings.put(value1, (someParams) -> { doAction1(someParams) });
5 actionMappings.put(value2, (someParams) -> { doAction2(someParams) });
6 actionMappings.put(value3, (someParams) -> { doAction3(someParams) });
7
8 actionMappings.get(param).apply(someParams);The example uses Java 8 lambda expressions; the idea can be applied with any language supporting maps.
Method Two: Chain of Responsibility
Introduction
When conditions are flexible, delegate the decision to a chain of handler objects.
Applicable Scenario
Variable conditional logic without a uniform representation.
Implementation and Example
Before refactoring:
1 public void handle(Request request) {
2 if (handlerA.canHandle(request)) {
3 handlerA.handleRequest(request);
4 } else if (handlerB.canHandle(request)) {
5 handlerB.handleRequest(request);
6 } else if (handlerC.canHandle(request)) {
7 handlerC.handleRequest(request);
8 }
9 }After refactoring (simplified):
1 public void handle(Request request) {
2 if (readOnly) { return; }
3 if (overCapacity()) { grow(); }
4 addElement(request);
5 }Method Three: Annotation‑driven
Introduction
Use language annotations (or similar mechanisms) to declare execution conditions, then invoke methods via reflection, chain, or table lookup.
Applicable Scenario
Many conditional branches where extensibility is a priority, typical in frameworks like Spring MVC.
Method Four: Event‑driven
Introduction
Associate events with handlers, achieving loose coupling between trigger and execution.
Applicable Scenario
One‑to‑many relationships, e.g., order payment triggering inventory, logistics, and points updates.
Method Five: Finite State Machine
Introduction
A state machine maps (state, event) pairs to actions, similar to table‑driven but with explicit state transitions.
Applicable Scenario
Protocol stacks, order processing, or any domain with well‑defined states.
Method Six: Optional
Introduction
Java 8's Optional can replace null‑check if…else branches.
Usage Example
1 String str = "Hello World!";
2 if (str != null) { System.out.println(str); } else { System.out.println("Null"); }Using Optional:
1 Optional<String> strOptional = Optional.of("Hello World!");
2 strOptional.ifPresentOrElse(System.out::println, () -> System.out.println("Null"));Method Seven: Assert
Introduction
Utility classes (e.g., Spring Assert, Apache Commons Validate) perform parameter validation, removing explicit if…else checks.
Method Eight: Polymorphism
Introduction
Replace conditional logic with subclass overrides, as described in Martin Fowler's Refactoring book.
Problem Two: Deeply Nested if…else
Method One: Extract Method
Move nested blocks into separate methods to flatten the call hierarchy.
1 public void add(Object element) {
2 if (readOnly) { return; }
3 if (overCapacity()) { grow(); }
4 addElement(element);
5 }Method Two: Guard Clauses
Replace nested conditionals with early returns.
1 double getPayAmount() {
2 if (_isDead) return deadAmount();
3 if (_isSeparated) return separatedAmount();
4 if (_isRetired) return retiredAmount();
5 return normalPayAmount();
6 }Problem Three: Complex Conditional Expressions
Complex boolean expressions should be extracted into well‑named methods or variables to improve readability.
Conclusion
The article presented ten (twelve with extensions) techniques to eliminate or simplify if…else statements, emphasizing that proper use of these patterns reflects a developer's mastery of refactoring, design patterns, and overall software architecture.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Top Architect
Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
