Skip to main content
Articles/architecture/exploring-the-strategy-design-pattern-simplifying-software-design
Series: Low-Level Design Interview Prepβ€’Part 6 of 17
Scenario Article

Strategy Design Pattern: Simplifying Software Design

Stop Writing If-Else Hell: The Case for Strategy

Abstract Algorithmsβ€’Feb 11, 2026
XLinkedInReddit
πŸ’‘ Executive Summary

The Strategy Pattern replaces giant if-else or switch blocks with a family of interchangeable algorithm classes.

🎯 System Scenario & Requirements

System Goal

The Strategy Pattern replaces giant if-else or switch blocks with a family of interchangeable algorithm classes.

Actors & Components

  • Application or API tier
  • Core service under design
  • Operator or SRE observer

Functional Requirements

  • TLDR: The Strategy Pattern replaces giant if-else or switch blocks with a family of interchangeable algorithm classes.
  • Each strategy is a self-contained unit that can be swapped at runtime without touching the client code.
  • The result: Open/Closed Principle compliance and dramatically easier testing.

Constraints & Assumptions

  • Use Strategy when: - You have multiple algorithms for the same task that differ only in behavior.
  • - You need to swap algorithms at runtime based on context or configuration.

⚑ Execution Stages & Decision Trace

1

Stage 1 β€” The Three Components of the Strategy Pattern

Action / Decision:

πŸ“Š Class Hierarchy: Strategy Pattern

Component Behavior:

πŸ“Š Class Hierarchy: Strategy Pattern

Observable Output: πŸ“Š Class Hierarchy: Strategy Pattern
Read Full Stage in Deep Dive ↓
πŸ“–

Deep Dive

Detailed Architectural Analysis

Scenario context

The Strategy Pattern replaces giant if-else or switch blocks with a family of interchangeable algorithm classes. Each strategy is a self-contained unit that can be swapped at runtime without touching the client code. The result: Open/Closed Principle compliance and dramatically easier testing.


Imagine a PaymentProcessor that handles Stripe, PayPal, Apple Pay, and crypto:

void pay(String type, in…

This walkthrough focuses on **Stop Writing If-Else Hell: The Case for Strategy**.

Before making a design choice, clarify:

- Imagine a PaymentProcessor that handles Stripe, PayPal, Apple Pay, and crypto: The problem: every new payment method forces you to modify the core class, violating the Open/Closed Principle ( open for extension, closed…
- Tests become fragile and the class grows without bound.
- The Strategy Pattern fixes this by extracting each algorithm into its own class.

**Concepts in play:** design-patterns-gof, jvm-internals-and-garbage-collection, graph-traversal-algorithms, api-security-oauth2

## Stage 1 β€” The Three Components of the Strategy Pattern

- πŸ“Š Class Hierarchy: Strategy Pattern

*Applies [**design-patterns-gof**](/concept/design-patterns-gof) to the decision above.*

```mermaid
graph TD
    A[Client: PaymentCheckout] --> B[Context: PaymentProcessor]
    B --> C{Strategy interface: PaymentStrategy}
    C --> D[StripeStrategy]
    C --> E[PayPalStrategy]
    C --> F[ApplePayStrategy]
classDiagram
  class Context {
    -strategy: PaymentStrategy
    +setStrategy(s: PaymentStrategy)
    +checkout(amount: int)
  }
  class PaymentStrategy {
    <>
    +pay(amount: int)
  }
  class StripeStrategy {
    -apiKey: String
    +pay(amount: int)
  }
  class PayPalStrategy {
    -email: String
    +pay(amount: int)
  }
  class CryptoStrategy {
    -walletAddress: String
    +pay(amount: int)
  }
  Context --> PaymentStrategy
  PaymentStrategy <|.. StripeStrategy
  PaymentStrategy <|.. PayPalStrategy
  PaymentStrategy <|.. CryptoStrategy

Observable outcome: πŸ“Š Class Hierarchy: Strategy Pattern

Stage 2 β€” Full Implementation: Payment Processing Example

  • Adding a new payment method (e.g., ApplePayStrategy ) requires zero changes to PaymentProcessor or any other existing class.
// 1. Strategy interface
interface PaymentStrategy {
    void pay(int amount);
}

// 2. Concrete strategies
class StripeStrategy implements PaymentStrategy {
    private String apiKey;
    public StripeStrategy(String apiKey) { this.apiKey = apiKey; }

    @Override
    public void pay(int amount) {
        System.out.printf("Charging $%d via Stripe (key: %s)%n", amount, apiKey);
        // real Stripe API call here
    }
}

class PayPalStrategy implements PaymentStrategy {
    private String email;
    public PayPalStrategy(String email) { this.email = email; }

    @Override
    public void pay(int amount) {
        System.out.printf("Sending $%d to %s via PayPal%n", amount, email);
    }
}

// 3. Context
class PaymentProcessor {
    private PaymentStrategy strategy;

    public PaymentProcessor(PaymentStrategy strategy) {
        this.strategy = strategy;
    }

    // Swap strategy at runtime
    public void setStrategy(PaymentStrategy strategy) {
        this.strategy = strategy;
    }

    public void checkout(int amount) {
        strategy.pay(amount);   // delegate entirely to the strategy
    }
}

// 4. Client usage
public class Main {
    public static void main(String[] args) {
        PaymentProcessor processor = new PaymentProcessor(new StripeStrategy("sk_test_123"));
        processor.checkout(99);     // β†’ Charging $99 via Stripe

        // Runtime swap: user switches to PayPal
        processor.setStrategy(new PayPalStrategy("user@example.com"));
        processor.checkout(49);     // β†’ Sending $49 to user@example.com via PayPal
    }
}

Observable outcome: Adding a new payment method (e.g., ApplePayStrategy ) requires zero changes to PaymentProcessor or any other existing class.

Stage 3 β€” Deep Dive: How Strategy Enables Open/Closed Design

  • The Strategy Pattern achieves Open/Closed compliance through composition over inheritance .
  • The Context holds an interface reference β€” not a concrete type β€” so any conforming object can be injected at runtime.
  • This is a direct application of dependency inversion : high-level modules (Context) depend on abstractions (Strategy interface), not on low-level implementations.
  • At runtime, the JVM dispatches pay() through a virtual method table β€” the same call site, different behavior, zero branching logic in the caller.

Observable outcome: At runtime, the JVM dispatches pay() through a virtual method table β€” the same call site, different behavior, zero branching logic in the caller.

Stage 4 β€” Runtime Strategy Switching Flow

  • The real power of the Strategy Pattern is runtime flexibility β€” the ability to swap the algorithm in the middle of execution without restarting the application or recompiling a single line of existing code.
  • Notice that the PaymentProcessor context class appears exactly once in this diagram and never changes shape.
  • All variation lives in the leaf nodes β€” the concrete strategy objects.
  • Adding CryptoStrategy is a new leaf, not a change to the graph.
graph TD
    A[Client Code] --> B[Context: PaymentProcessor created with StripeStrategy]
    B --> C{User changes payment method?}
    C -->|No| D[context.checkout called  delegates to StripeStrategy.pay]
    C -->|Yes| E[context.setStrategy called with new PayPalStrategy]
    E --> F[context.checkout called  delegates to PayPalStrategy.pay]
    D --> G[Payment result returned]
    F --> G

Observable outcome: Adding CryptoStrategy is a new leaf, not a change to the graph.

Stage 5 β€” Real-World Applications: Where the Strategy Pattern Appears in Real Systems

Observable outcome: You can explain how β€œReal-World Applications: Where the Strategy Pattern Appears in Real Systems” changes the architecture or implementation path.

Stage 6 β€” What to Learn Next

    • Low-Level Design (LLD) for Tic-Tac-Toe - Low-Level Design Guide for Ride Booking - The Ultimate Data Structures Cheat Sheet

Observable outcome: - Low-Level Design (LLD) for Tic-Tac-Toe - Low-Level Design Guide for Ride Booking - The Ultimate Data Structures Cheat Sheet

End-to-end outcome

Imagine a PaymentProcessor that handles Stripe, PayPal, Apple Pay, and crypto: The problem: every new payment method forces you to modify the core class, violating the Open/Closed Principle ( open for extension, closed for modification ).

Alternatives considered

Decision Guide: Strategy vs. Similar Patterns

Decision Guide: Strategy vs. Similar Patterns

Failure and degraded modes

Trade-offs & Failure Modes: When to Use Strategy (and When Not To)

  • Detection: Use Strategy when: - You have multiple algorithms for the same task that differ only in behavior.
  • User or operator impact: Use Strategy when: - You have multiple algorithms for the same task that differ only in behavior.

Share this case study

Discuss architecture trade-offs and lessons with your team.

Share:XLinkedInReddit