Exploring the Strategy Design Pattern: Simplifying Software Design
Abstract AlgorithmsTL;DR
TLDR: If your code is full of if (type == A) doThis() else if (type == B) doThat(), you need the Strategy Pattern. It allows you to define a family of algorithms, encapsulate each one, and make them interchangeable at runtime. What is the Strategy ...

TLDR: If your code is full of
if (type == A) doThis() else if (type == B) doThat(), you need the Strategy Pattern. It allows you to define a family of algorithms, encapsulate each one, and make them interchangeable at runtime.
What is the Strategy Pattern? (The "No-Jargon" Explanation)
Imagine a GPS Navigation App.
- The Goal: Go from Home to the Airport.
- The Strategies:
- Car: Fastest route, uses highways.
- Walking: Shortest distance, uses sidewalks/parks.
- Public Transport: Uses bus/train lines.
You don't buy a separate phone for each mode. You just tap a button to switch the "Strategy." The destination remains the same; the algorithm to get there changes.
In code, instead of writing one giant function with 3 if blocks, we create 3 separate classes (CarStrategy, WalkStrategy, BusStrategy) and plug them into the GPS.
1. The Problem: The "If-Else" Hell
Let's say we are building an E-commerce Payment System.
public class PaymentService {
public void pay(String type, int amount) {
if (type.equals("CREDIT_CARD")) {
System.out.println("Validating Card...");
System.out.println("Charging " + amount);
} else if (type.equals("PAYPAL")) {
System.out.println("Redirecting to PayPal...");
System.out.println("Charging " + amount);
} else if (type.equals("CRYPTO")) {
System.out.println("Checking Wallet Balance...");
System.out.println("Charging " + amount);
}
}
}
Why is this bad?
- Hard to Maintain: If you want to add "Apple Pay", you have to modify the core class (Open/Closed Principle violation).
- Hard to Test: You have to test all logic in one massive file.
2. The Solution: The Strategy Pattern
We break this into 3 parts:
- The Interface (Strategy): Defines the common action (
pay). - The Concrete Strategies: The actual implementations.
- The Context: The class that uses the strategy.
Step 1: The Interface
public interface PaymentStrategy {
void pay(int amount);
}
Step 2: The Implementations
public class CreditCardStrategy implements PaymentStrategy {
public void pay(int amount) {
System.out.println("Paid " + amount + " via Credit Card.");
}
}
public class PayPalStrategy implements PaymentStrategy {
public void pay(int amount) {
System.out.println("Paid " + amount + " via PayPal.");
}
}
Step 3: The Context (Usage)
public class ShoppingCart {
private PaymentStrategy strategy;
// We can swap the strategy at runtime!
public void setPaymentStrategy(PaymentStrategy strategy) {
this.strategy = strategy;
}
public void checkout(int amount) {
strategy.pay(amount);
}
}
Deep Dive: How It Works at Runtime
Let's trace how the data flows when a user switches payment methods.
Toy Scenario: A User Checking Out
| Step | User Action | Code Execution | Object in Memory |
| 1 | Selects "Credit Card" | cart.setPaymentStrategy(new CreditCardStrategy()) | strategy points to a CreditCard object. |
| 2 | Clicks "Pay $100" | cart.checkout(100) -> strategy.pay(100) | Executes CreditCardStrategy.pay(). |
| 3 | Changes mind to "PayPal" | cart.setPaymentStrategy(new PayPalStrategy()) | strategy pointer updates to PayPal object. |
| 4 | Clicks "Pay $100" | cart.checkout(100) -> strategy.pay(100) | Executes PayPalStrategy.pay(). |
The Math (Complexity Reduction):
- Before: Complexity = $O(N)$ inside one method (where N is number of payment types).
- After: Complexity = $O(1)$ inside the Context. The complexity is distributed across N small classes.
Real-World Application: Game Character Skills
- Context: A Role-Playing Game (RPG).
- Problem: A character can equip different weapons.
- Sword: Attack = "Slash"
- Bow: Attack = "Shoot Arrow"
- Magic Staff: Attack = "Cast Fireball"
- Implementation:
Characterclass has aWeaponStrategyfield.- When the player opens the inventory and equips a Bow, we call
character.setWeapon(new BowStrategy()). - When the player presses "X" to attack, the code simply runs
weapon.attack(). The Character class doesn't care how the attack happens, just that it happens.
Summary & Key Takeaways
- Encapsulation: Each algorithm lives in its own class.
- Interchangeability: You can swap behavior at runtime (e.g., changing weapons, changing payment methods).
- Open/Closed Principle: You can add a new strategy (e.g., "BitcoinStrategy") without changing the existing code.
Practice Quiz: Test Your Design Skills
Scenario: You are writing a sorting library. You want the user to be able to choose between "QuickSort" (for speed) and "MergeSort" (for stability). Which pattern fits best?
- A) Singleton Pattern
- B) Strategy Pattern
- C) Factory Pattern
Scenario: You have a
Duckclass. Some ducks fly, some swim, some are rubber ducks. You want to handle thefly()behavior dynamically.- A) Create a
FlyStrategyinterface withFlyWithWingsandNoFlyimplementations. - B) Put a giant
if-elseblock in theDuckclass. - C) Create a separate subclass for every single combination (RubberDuck, MallardDuck, RedheadDuck...).
- A) Create a
Scenario: What is the main disadvantage of the Strategy Pattern?
- A) It makes the code harder to test.
- B) It increases the number of classes in the project.
- C) It violates the Open/Closed Principle.
(Answers: 1-B, 2-A, 3-B)
Tags

Written by
Abstract Algorithms
@abstractalgorithms
