100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Software Design

Strategy Pattern

The Strategy pattern encapsulates interchangeable algorithms behind a common interface so a context object can swap behavior at runtime without conditional logic.

Behavioral Patterns IIntermediate9 min readJul 10, 2026
Analogies

What Is the Strategy Pattern?

The Strategy pattern defines a family of interchangeable algorithms, encapsulates each one in its own class, and lets client code swap between them at runtime through a common interface. Instead of embedding conditional logic that picks behavior based on type checks, the context object holds a reference to a Strategy interface and delegates the actual work to whichever concrete implementation was injected, so new algorithms can be added without touching existing code.

🏏

Cricket analogy: A captain choosing between a leg-spinner like Rashid Khan and a pacer like Bumrah for the death overs is swapping bowling strategies without changing the match rules themselves, exactly how a Context swaps ConcreteStrategy objects.

Structure and Participants

The pattern has three participants: the Context, which maintains a reference to a Strategy object and delegates to it; the Strategy interface, which declares a common method such as execute() or calculate(); and one or more ConcreteStrategy classes that implement that method with a specific algorithm. The Context is typically configured via constructor injection or a setter, so the choice of algorithm can be made at object-creation time or changed dynamically during the object's lifetime.

🏏

Cricket analogy: The team management (Context) hands the ball to whichever bowler (ConcreteStrategy) implements the 'deliver an over' interface, and can switch bowlers between overs without redesigning the scoreboard.

typescript
interface PaymentStrategy {
  pay(amountCents: number): string;
}

class CreditCardStrategy implements PaymentStrategy {
  constructor(private cardNumber: string) {}
  pay(amountCents: number): string {
    return `Charged ${amountCents} cents to card ending ${this.cardNumber.slice(-4)}`;
  }
}

class UpiStrategy implements PaymentStrategy {
  constructor(private upiId: string) {}
  pay(amountCents: number): string {
    return `Debited ${amountCents} cents via UPI id ${this.upiId}`;
  }
}

class Checkout {
  private strategy: PaymentStrategy;
  constructor(strategy: PaymentStrategy) {
    this.strategy = strategy;
  }
  setStrategy(strategy: PaymentStrategy) {
    this.strategy = strategy;
  }
  completeOrder(amountCents: number): string {
    return this.strategy.pay(amountCents);
  }
}

const checkout = new Checkout(new CreditCardStrategy('4242424242424242'));
console.log(checkout.completeOrder(1999));
checkout.setStrategy(new UpiStrategy('user@bank'));
console.log(checkout.completeOrder(4599));

When to Use It vs Alternatives

Strategy shines when a class has several variants of an algorithm that would otherwise require a large conditional block (if/else or switch) scattered through the codebase, especially when new variants get added over time. Replacing that conditional with polymorphism satisfies the Open/Closed Principle: adding a new strategy means writing a new class, not editing existing branching logic, which reduces regression risk in code that's already been tested.

🏏

Cricket analogy: Instead of an umpire mentally running through a giant if/else for every possible dismissal type, each dismissal (LBW, caught, run-out) is handled by its own reviewable rule, so adding a new technology-assisted rule like the 'soft signal' doesn't touch the existing ones.

The Open/Closed Principle (the 'O' in SOLID) states classes should be open for extension but closed for modification. Strategy is one of the clearest implementations of this: adding behavior means adding a class, not editing a tested one.

Strategy vs State Pattern

Strategy and State share the same UML shape — a context delegating to a pluggable object through a common interface — but they differ in intent and lifecycle. In Strategy, the client typically picks the algorithm once and it stays fixed for that context's lifetime, reflecting a client-driven choice; in State, the state objects themselves trigger transitions to other states in response to events, so the context's behavior evolves autonomously as the object moves through its lifecycle.

🏏

Cricket analogy: Choosing a bowler for an over (Strategy) is a captain's one-off tactical call, whereas a match's state — first innings, second innings, super over — transitions on its own as overs are bowled, without anyone 'choosing' the next phase.

If your 'ConcreteStrategy' classes start referencing each other and switching themselves based on internal conditions, you're no longer implementing Strategy — you've drifted into State. Rename and restructure so transition logic is explicit.

  • Strategy encapsulates interchangeable algorithms behind one interface, letting a Context swap behavior via composition rather than conditionals.
  • The three participants are Context, Strategy interface, and one or more ConcreteStrategy classes.
  • It replaces sprawling if/else or switch blocks with polymorphism, satisfying the Open/Closed Principle.
  • Strategies should be stateless and unaware of each other; they don't trigger transitions to other strategies.
  • The Context can be configured with a strategy at construction time or reassigned dynamically via a setter.
  • Strategy and State share the same structural shape but differ in intent: client-chosen algorithm vs self-transitioning behavior.
  • A common tell that you've built State instead of Strategy is when 'strategies' start switching themselves based on internal events.

Practice what you learned

Was this page helpful?

Topics covered

#SoftwareDesign#DesignPatternsStudyNotes#SoftwareEngineering#StrategyPattern#Strategy#Pattern#Structure#Participants#StudyNotes#SkillVeris