OOP Concepts · Design Patterns

Strategy Pattern

Strategy is about choice: several ways to do the same job (calculate shipping, apply a discount, sort a list), each in its own class, swappable at any time.

Advanced 5 min Lesson 31 of 38 Runnable examplesExamples use modern JavaScript (ES2022 private fields) and C# 13.
0/38 in OOP Concepts
On this page · 5 min · Advanced

In simple words#

Google Maps lets you choose car, bike or walking — same trip, different strategy. Swap algorithms without changing the code that uses them.

Strategy
Interchangeable algorithms behind one interface.
Context
The class that uses a strategy.
Benefit
Add strategies without editing the context.

Learning objectives#

  • Explain the problem the Strategy pattern solves
  • Implement strategies as classes with a common method
  • Switch strategies at runtime
  • Relate Strategy to Open/Closed and composition

Explanation#

The problem: a method with a big if/else for each variation of an algorithm. Every new variation makes it longer and riskier.

The pattern: define one interface (cost(weight)), write one class per algorithm, and give the context object (ShippingCalculator) a strategy to use.

The context delegates the work to its current strategy and can switch strategy at runtime.

Strategy is composition plus Open/Closed in action: new algorithms are new classes; the calculator never changes.

In JavaScript, a strategy can also be a plain function — Array.prototype.sort((a, b) => ...) takes a comparison strategy.

Syntax#

C# Illustrative
public interface IShippingStrategy { string Name { get; } decimal Cost(decimal weightKg); }


public class StandardShipping : IShippingStrategy
{
    public string Name => "Standard";
    public decimal Cost(decimal kg) => 40 + kg * 10;
}


public class ShippingCalculator(IShippingStrategy strategy)
{
    public IShippingStrategy Strategy { get; set; } = strategy;
    public string Quote(decimal kg) => 
quot;
{Strategy.Name}: {Strategy.Cost(kg)}";
}

Code example: Shipping options#

JavaScriptShipping options Runnable
class StandardShipping {
  name = 'Standard';
  cost(weightKg) { return 40 + weightKg * 10; }
}
class ExpressShipping {
  name = 'Express';
  cost(weightKg) { return 100 + weightKg * 20; }
}
class StorePickup {
  name = 'Store pickup';
  cost() { return 0; }
}


class ShippingCalculator {
  constructor(strategy) { this.strategy = strategy; }
  setStrategy(strategy) { this.strategy = strategy; }
  quote(weightKg) { return `${this.strategy.name}: ₹${this.strategy.cost(weightKg)}`; }
}


const calc = new ShippingCalculator(new StandardShipping());
console.log(calc.quote(2));
calc.setStrategy(new ExpressShipping());
console.log(calc.quote(2));
calc.setStrategy(new StorePickup());
console.log(calc.quote(2));


const options = [new StandardShipping(), new ExpressShipping(), new StorePickup()];
const cheapest = options.reduce((best, s) => (s.cost(2) < best.cost(2) ? s : best));
console.log('Cheapest:', cheapest.name);
Expected output
Standard: ₹60
Express: ₹140
Store pickup: ₹0
Cheapest: Store pickup
Opens this example in the full playground

Step-by-step explanation#

  1. Each shipping class is a strategy with the same shape: a name and cost(weightKg).
  2. ShippingCalculator holds one strategy and delegates quote() to it.
  3. setStrategy swaps the algorithm at runtime — the calculator code is unchanged.
  4. Because all strategies share an interface, comparing them is a one-line reduce.

Real-world use case#

An ASP.NET Core checkout injects IEnumerable<IShippingStrategy>, shows every option with its price, and uses the one the customer picks. Adding “Same-day delivery” is a new class and one DI registration.

Common mistakes#

Strategies that need to know about each other

Each strategy should work alone. If one checks another’s type, the abstraction is wrong.

Strategy for a single algorithm

If there is only ever one way, a plain method is simpler.

Forgetting a default

A context with no strategy crashes on first use. Require one in the constructor.

Best practices#

  • Give all strategies the same small interface
  • Require a strategy in the constructor
  • Register strategies with DI and select by key
  • Use plain functions as strategies when no state is needed

Remember this#

Strategy = same job, many interchangeable ways.

Context delegates; strategies do the work.

New algorithm = new class, no edits.

Practice exercise#

Your task: Create a TextFormatter that uses a strategy to format a string: UpperCaseStrategy, LowerCaseStrategy and TitleCaseStrategy. Format "hello oop world" with each.

Need a hint?
Title case: split on spaces and upper-case the first letter of each word.
Show solution
JavaScript Runnable
class UpperCaseStrategy { format(t) { return t.toUpperCase(); } }
class LowerCaseStrategy { format(t) { return t.toLowerCase(); } }
class TitleCaseStrategy {
  format(t) { return t.split(' ').map(w => w[0].toUpperCase() + w.slice(1)).join(' '); }
}
class TextFormatter {
  constructor(strategy) { this.strategy = strategy; }
  format(text) { return this.strategy.format(text); }
}
for (const s of [new UpperCaseStrategy(), new LowerCaseStrategy(), new TitleCaseStrategy()]) {
  console.log(new TextFormatter(s).format('hello oop world'));
}
// HELLO OOP WORLD
// hello oop world
// Hello Oop World
Opens this example in the full playground

Mini quiz#

Strategy Pattern quizMultiple choice Question 1 of 3

What does the Strategy pattern let you do?

Score so far: 0/0

Want more? Practise all OOP Concepts MCQs · OOP Concepts interview questions

My notes#

Saved in this browser.

Progress is saved in this browser. Sign in to sync.