Java
Replacing Conditionals with Polymorphism
Complex conditional logic is one of the most common sources of bugs and maintenance headaches. This article covers techniques to replace switch statements and if-else chains with polymorphic designs that are easier to extend, test, and maintain.
📋 At a Glance
| Aspect | Details |
|---|---|
| Patterns Covered | Strategy, State, Pattern Matching |
| Primary Benefit | Open for extension, closed for modification |
| Best For | Type-based switches, algorithm selection |
| Java Version | 8+ (lambdas), 17+ (pattern matching) |
| Risk Level | Medium (requires careful testing) |
🎯 What You'll Learn
- Strategy Pattern for algorithm selection
- State Pattern for state-dependent behavior
- Pattern Matching (Java 17+) for concise conditionals
- Enum-based dispatch for simple cases
- Rule Engine Pattern for complex business rules
- When to keep conditionals (not everything needs polymorphism)
🔥 Production Story: The Payment Switch That Grew Out of Control
A payment service started with a simple switch:
JAVA(8 lines)CodeLoading syntax highlighter...
Three years later:
JAVA(16 lines)CodeLoading syntax highlighter...
The problems:
- Adding payment type required changes in 12 switch statements across codebase
- Each payment type's code mixed together (no isolation)
- Testing required understanding entire switch
- Different teams couldn't work independently
The fix: Strategy Pattern
JAVA(13 lines)CodeLoading syntax highlighter...
🧠 Mental Model: Conditional to Polymorphism
TEXT(25 lines)CodeLoading syntax highlighter...
🔬 Deep Dive
Pattern 1: Strategy Pattern
Use when: Different algorithms can be selected at runtime.
JAVA(75 lines)CodeLoading syntax highlighter...
Adding new shipping method:
- Create new
@Componentclass implementingShippingCalculator - That's it! Spring auto-discovers and registers it.
Pattern 2: State Pattern
Use when: Object behavior depends on its current state.
JAVA(102 lines)CodeLoading syntax highlighter...
Pattern 3: Pattern Matching (Java 17+)
Use when: Need concise conditionals, fixed set of types.
JAVA(39 lines)CodeLoading syntax highlighter...
Pattern 4: Enum with Behavior
Use when: Simple cases, limited number of fixed types.
JAVA(32 lines)CodeLoading syntax highlighter...
Pattern 5: Rule Engine for Complex Conditions
Use when: Many complex, changeable business rules.
JAVA(57 lines)CodeLoading syntax highlighter...
⚠️ Common Mistakes
Mistake 1: Over-Engineering Simple Cases
JAVA(9 lines)CodeLoading syntax highlighter...
Mistake 2: Forgetting Null/Default Cases
JAVA(14 lines)CodeLoading syntax highlighter...
🐛 Debug This: The Missing Discount Type
A developer reports: "We added a new discount type LOYALTY_POINTS but customers aren't getting their discounts. The switch compiles fine!"
JAVA(27 lines)CodeLoading syntax highlighter...
Why doesn't the compiler catch this bug? How would you prevent it?
✅ Solution:
The bug is the silent default case - when a new enum value is added, it falls through to
default instead of causing a compile error.Problems:
defaultcase swallows new enum values silently- No compiler warning for unhandled enum cases
- Business logic silently fails
Prevention strategies:
JAVA(19 lines)CodeLoading syntax highlighter...
The lesson: Avoid
default cases in enum switches when possible. Use exhaustive switches (Java 17+) or Strategy pattern to ensure all cases are handled.💻 Exercises
Exercise 1: Convert Switch to Strategy
⭐⭐ Difficulty: Medium | ⏱️ Time: 15 minutes
Task: Refactor this switch to Strategy pattern.
JAVA(9 lines)CodeLoading syntax highlighter...
✅ Solution:
JAVA(27 lines)CodeLoading syntax highlighter...
Exercise 2: Apply State Pattern
⭐⭐ Difficulty: Medium | ⏱️ Time: 20 minutes
Task: Replace these conditionals with State pattern.
JAVA(21 lines)CodeLoading syntax highlighter...
✅ Solution:
JAVA(25 lines)CodeLoading syntax highlighter...
Exercise 3: Use Enum with Behavior
⭐⭐ Difficulty: Medium | ⏱️ Time: 15 minutes
Task: Add behavior to this enum to eliminate the switch.
JAVA(12 lines)CodeLoading syntax highlighter...
✅ Solution:
JAVA(21 lines)CodeLoading syntax highlighter...
Exercise 4: Pattern Matching (Java 17+)
⭐⭐⭐ Difficulty: Medium-Hard | ⏱️ Time: 20 minutes
Task: Refactor to use pattern matching.
JAVA(13 lines)CodeLoading syntax highlighter...
✅ Solution:
JAVA(10 lines)CodeLoading syntax highlighter...
Exercise 5: Rule Engine Pattern
⭐⭐⭐⭐ Difficulty: Hard | ⏱️ Time: 25 minutes
Task: Replace nested conditionals with Rule Engine.
JAVA(14 lines)CodeLoading syntax highlighter...
✅ Solution:
JAVA(38 lines)CodeLoading syntax highlighter...
🎤 Senior-Level Interview Questions
Q1: When should you keep a switch instead of using polymorphism?
Answer:
- Few cases (2-3) that won't grow
- Simple, one-liner cases
- Performance-critical code
- Pattern matching is cleaner (Java 17+)
- Cases aren't really "types" but just values
Q2: Strategy vs State - what's the difference?
Answer:
- Strategy: Client chooses which algorithm to use
- State: Object changes its own behavior based on internal state
- Strategy = same object, different algorithms
- State = same algorithm, different behavior per state
📝 Summary
| Approach | Use When |
|---|---|
| Strategy | Algorithm selection, extensible |
| State | Behavior depends on state |
| Pattern Matching | Fixed types, Java 17+ |
| Enum with behavior | Simple, fixed cases |
| Rule Engine | Complex business rules |
| Keep Switch | 2-3 simple cases |
📅 Review Schedule for This Article
| Day | Task | Time |
|---|---|---|
| Day 1 | Review Strategy vs State vs Pattern Matching table | 5 min |
| Day 3 | Redo Exercise 1 (Convert Switch to Strategy) | 15 min |
| Day 7 | Answer interview questions without looking | 10 min |
| Day 14 | Redo Debug This (Missing Discount Type) | 10 min |
| Day 30 | Find one switch in your project that could use Strategy | 15 min |
Next in series: [Part 8: Null Handling Patterns]