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.
On this page · 5 min · Advanced
On this page
- In simple words
- Learning objectives
- Explanation
- Syntax
- Code example
- Step by step
- Real-world use
- Common mistakes
- Best practices
- Remember this
- Practice exercise
- Mini quiz
- Related topics
- My notes
- Reading time
- 5 min
- Difficulty
- Advanced
- Updated
- Oct 28, 2026
Related tutorials
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#
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#
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);
Standard: ₹60 Express: ₹140 Store pickup: ₹0 Cheapest: Store pickup
Step-by-step explanation#
- Each shipping class is a strategy with the same shape: a name and cost(weightKg).
- ShippingCalculator holds one strategy and delegates quote() to it.
- setStrategy swaps the algorithm at runtime — the calculator code is unchanged.
- 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#
Each strategy should work alone. If one checks another’s type, the abstraction is wrong.
If there is only ever one way, a plain method is simpler.
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?
Show solution
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
Mini quiz#
What does the Strategy pattern let you do?
Want more? Practise all OOP Concepts MCQs · OOP Concepts interview questions
My notes#
Saved in this browser.