Strategy Design Pattern (Behavioral)
Strategy is a behavioral design pattern that lets you define a family of algorithms, put each of them into a separate class, and make their objects interchangeable.
Problem
Section titled βProblemβImagine youβre creating a navigation app for casual travelers. One of the primary features is an automatic route planner. A user should be able to enter an address and see the fastest route to their destination displayed on the map.
The first version of the app could only build routes over roads. People traveling by car were ecstatic. But soon, you realize some users prefer walking, while others use public transport or bicycles.
Each time you added a new routing algorithm, the main class of the navigator doubled in size. At some point, the beast became too hard to maintain. Any change to one of the algorithms affected the whole class, increasing the chance of creating an error in already-working code.
Solution
Section titled βSolutionβThe Strategy pattern suggests that you take a class that does something specific in a lot of different ways and extract all of these algorithms into separate classes called strategies.
The original class, called context, must have a field for storing a reference to one of the strategies. The context delegates the work to a linked strategy object instead of executing it on its own.
The context isnβt responsible for selecting an appropriate algorithm for the job. Instead, the client passes the desired strategy to the context. In fact, the context doesnβt know much about strategies; it works with all of them through the same generic interface.
Real-World Analogy
Section titled βReal-World AnalogyβImagine you have to get to the airport. You can catch a bus, order a cab, or get on a bicycle. These are your transportation strategies. You can choose one of the strategies depending on factors such as budget or time constraints.
Structure
Section titled βStructureβ- Context maintains a reference to one of the concrete strategies and communicates with this object only via the strategy interface.
- Strategy interface is common to all concrete strategies. It declares a method the context uses to execute a strategy.
- Concrete Strategies implement different variations of an algorithm the context uses.
- Client creates a specific strategy object and passes it to the context. The context exposes a setter which lets clients replace the strategy associated with the context at runtime.
Structure (Class Diagram)
Section titled βStructure (Class Diagram)βclassDiagram
class Context {
-strategy: Strategy
+setStrategy(strategy: Strategy)
+executeStrategy(data)
}
class Strategy {
<<interface>>
+execute(data)
}
class ConcreteStrategyA {
+execute(data)
}
class ConcreteStrategyB {
+execute(data)
}
Context o-- Strategy
Strategy <|-- ConcreteStrategyA
Strategy <|-- ConcreteStrategyB
Client ..> Context
Client ..> ConcreteStrategyA
Pseudocode
Section titled βPseudocodeβ# The strategy interface declares operations common to all# supported versions of some algorithm.interface RouteStrategy: method buildRoute(A, B)
# Concrete strategies implement the algorithm while following# the base strategy interface.class RoadStrategy implements RouteStrategy: method buildRoute(A, B): # Implementation for road routing.
class WalkingStrategy implements RouteStrategy: method buildRoute(A, B): # Implementation for walking paths.
# The context maintains a reference to one of the strategy# objects.class Navigator: private field strategy: RouteStrategy
method setStrategy(strategy): this.strategy = strategy
method buildRoute(A, B): strategy.buildRoute(A, B)Applicability
Section titled βApplicabilityβ- Variant Algorithms: Use the Strategy pattern when you want to use different variants of an algorithm within an object and be able to switch from one algorithm to another during runtime.
- Similar Classes: Use the Strategy when you have a lot of similar classes that only differ in the way they execute some behavior.
- Isolate Business Logic: Use the pattern to isolate the business logic of a class from the implementation details of algorithms that may not be as important in the context of that logic.
- Hide Massive Conditionals: Use the pattern when your class has a massive conditional operator that switches between different variants of the same algorithm.
How to Implement
Section titled βHow to Implementβ- In the context class, identify an algorithm thatβs prone to frequent changes.
- Declare the strategy interface common to all variants of that algorithm.
- One by one, extract all algorithms into their own classes. They should all implement the strategy interface.
- In the context class, add a field for storing a reference to a strategy object.
- Clients of the context must associate it with a suitable strategy that matches the way they expect the context to perform its primary job.
Pros and Cons
Section titled βPros and ConsβPros:
- You can swap algorithms used inside an object at runtime.
- You can isolate the implementation details of an algorithm from the code that uses it.
- You can replace inheritance with composition.
- Open/Closed Principle: You can introduce new strategies without having to change the context.
Cons:
- If you only have a couple of algorithms and they rarely change, thereβs no real reason to overcomplicate the program with new classes and interfaces.
- Clients must be aware of the differences between strategies to be able to select a proper one.
- A lot of modern programming languages have functional type support that lets you implement different versions of an algorithm inside a set of anonymous functions.
Relations with other Design Patterns
Section titled βRelations with other Design Patternsβ- Bridge, Strategy, and State have very similar structures based on composition. However, they all solve different problems.
- Strategy changes the guts of an object, while Decorator changes the skin.
- Template Method is based on inheritance: it lets you alter parts of an algorithm by extending those parts in subclasses. Strategy is based on composition: you can alter parts of the objectβs behavior by supplying it with different strategies.
Code Examples
Section titled βCode ExamplesβCode Examples (C#)
Section titled βCode Examples (C#)β// Strategypublic interface IStrategy{ int Execute(int a, int b);}
// Concrete Strategiespublic class AddStrategy : IStrategy{ public int Execute(int a, int b) => a + b;}
public class SubtractStrategy : IStrategy{ public int Execute(int a, int b) => a - b;}
public class MultiplyStrategy : IStrategy{ public int Execute(int a, int b) => a * b;}
// Contextpublic class Calculator{ private IStrategy _strategy;
public void SetStrategy(IStrategy strategy) { _strategy = strategy; }
public int Calculate(int a, int b) { return _strategy.Execute(a, b); }}
// Usageclass Program{ static void Main() { var calc = new Calculator();
calc.SetStrategy(new AddStrategy()); Console.WriteLine(calc.Calculate(5, 3)); // 8
calc.SetStrategy(new MultiplyStrategy()); Console.WriteLine(calc.Calculate(5, 3)); // 15 }}Code Examples (Python)
Section titled βCode Examples (Python)βfrom __future__ import annotationsfrom abc import ABC, abstractmethodfrom typing import List
class Context(): """ The Context defines the interface of interest to clients. """ def __init__(self, strategy: Strategy) -> None: self._strategy = strategy
@property def strategy(self) -> Strategy: return self._strategy
@strategy.setter def strategy(self, strategy: Strategy) -> None: self._strategy = strategy
def do_some_business_logic(self) -> None: print("Context: Sorting data using the strategy (not sure how it'll do it)") result = self._strategy.do_algorithm(["a", "b", "c", "d", "e"]) print(",".join(result))
class Strategy(ABC): """ The Strategy interface declares operations common to all supported versions of some algorithm. """ @abstractmethod def do_algorithm(self, data: List): pass
class ConcreteStrategyA(Strategy): def do_algorithm(self, data: List) -> List: return sorted(data)
class ConcreteStrategyB(Strategy): def do_algorithm(self, data: List) -> List: return reversed(sorted(data))
if __name__ == "__main__": # The client code picks a concrete strategy and passes it to the context. context = Context(ConcreteStrategyA()) print("Client: Strategy is set to normal sorting.") context.do_some_business_logic() print()
print("Client: Strategy is set to reverse sorting.") context.strategy = ConcreteStrategyB() context.do_some_business_logic()Code Examples (C++)
Section titled βCode Examples (C++)β#include <iostream>#include <memory>
// Strategyclass IStrategy {public: virtual ~IStrategy() = default; virtual int Execute(int a, int b) = 0;};
// Concrete Strategiesclass AddStrategy : public IStrategy {public: int Execute(int a, int b) override { return a + b; }};
class SubtractStrategy : public IStrategy {public: int Execute(int a, int b) override { return a - b; }};
class MultiplyStrategy : public IStrategy {public: int Execute(int a, int b) override { return a * b; }};
// Contextclass Calculator {private: std::unique_ptr<IStrategy> strategy;
public: void SetStrategy(std::unique_ptr<IStrategy> s) { strategy = std::move(s); }
int Calculate(int a, int b) { return strategy->Execute(a, b); }};
// Usageint main() { Calculator calc;
calc.SetStrategy(std::make_unique<AddStrategy>()); std::cout << calc.Calculate(5, 3) << std::endl; // 8
calc.SetStrategy(std::make_unique<MultiplyStrategy>()); std::cout << calc.Calculate(5, 3) << std::endl; // 15
return 0;}Code Examples (Java)
Section titled βCode Examples (Java)β// Strategypublic interface IStrategy { int execute(int a, int b);}
// Concrete Strategiespublic class AddStrategy implements IStrategy { @Override public int execute(int a, int b) { return a + b; }}
public class SubtractStrategy implements IStrategy { @Override public int execute(int a, int b) { return a - b; }}
public class MultiplyStrategy implements IStrategy { @Override public int execute(int a, int b) { return a * b; }}
// Contextpublic class Calculator {
private IStrategy strategy;
public void setStrategy(IStrategy strategy) { this.strategy = strategy; }
public int calculate(int a, int b) { return strategy.execute(a, b); }}
// Usagepublic class Program { public static void main(String[] args) { Calculator calc = new Calculator();
calc.setStrategy(new AddStrategy()); System.out.println(calc.calculate(5, 3)); // 8
calc.setStrategy(new MultiplyStrategy()); System.out.println(calc.calculate(5, 3)); // 15 }}Code Examples (JavaScript)
Section titled βCode Examples (JavaScript)β// Strategy (simulated interface)class IStrategy { execute(a, b) { throw new Error("execute() must be implemented"); }}
// Concrete Strategiesclass AddStrategy extends IStrategy { execute(a, b) { return a + b; }}
class SubtractStrategy extends IStrategy { execute(a, b) { return a - b; }}
class MultiplyStrategy extends IStrategy { execute(a, b) { return a * b; }}
// Contextclass Calculator { setStrategy(strategy) { this.strategy = strategy; }
calculate(a, b) { return this.strategy.execute(a, b); }}
// Usageconst calc = new Calculator();
calc.setStrategy(new AddStrategy());console.log(calc.calculate(5, 3)); // 8
calc.setStrategy(new MultiplyStrategy());console.log(calc.calculate(5, 3)); // 15Code Examples (Visual Basic)
Section titled βCode Examples (Visual Basic)β' ============================' Strategy' ============================Public Interface IStrategy Function Execute(a As Integer, b As Integer) As IntegerEnd Interface
' ============================' Concrete Strategies' ============================Public Class AddStrategy Implements IStrategy
Public Function Execute(a As Integer, b As Integer) As Integer Implements IStrategy.Execute Return a + b End FunctionEnd Class
Public Class SubtractStrategy Implements IStrategy
Public Function Execute(a As Integer, b As Integer) As Integer Implements IStrategy.Execute Return a - b End FunctionEnd Class
Public Class MultiplyStrategy Implements IStrategy
Public Function Execute(a As Integer, b As Integer) As Integer Implements IStrategy.Execute Return a * b End FunctionEnd Class
' ============================' Context' ============================Public Class Calculator Private _strategy As IStrategy
Public Sub SetStrategy(strategy As IStrategy) _strategy = strategy End Sub
Public Function Calculate(a As Integer, b As Integer) As Integer Return _strategy.Execute(a, b) End FunctionEnd Class
' ============================' Usage' ============================Module Program Sub Main() Dim calc As New Calculator()
calc.SetStrategy(New AddStrategy()) Console.WriteLine(calc.Calculate(5, 3)) ' 8
calc.SetStrategy(New MultiplyStrategy()) Console.WriteLine(calc.Calculate(5, 3)) ' 15 End SubEnd ModuleCode Examples (Rust)
Section titled βCode Examples (Rust)β// Strategytrait Strategy { fn execute(&self, a: i32, b: i32) -> i32;}
// Concrete Strategiesstruct AddStrategy;struct SubtractStrategy;struct MultiplyStrategy;
impl Strategy for AddStrategy { fn execute(&self, a: i32, b: i32) -> i32 { a + b }}
impl Strategy for SubtractStrategy { fn execute(&self, a: i32, b: i32) -> i32 { a - b }}
impl Strategy for MultiplyStrategy { fn execute(&self, a: i32, b: i32) -> i32 { a * b }}
// Contextstruct Calculator { strategy: Box<dyn Strategy>,}
impl Calculator { fn new(strategy: Box<dyn Strategy>) -> Self { Self { strategy } }
fn set_strategy(&mut self, strategy: Box<dyn Strategy>) { self.strategy = strategy; }
fn calculate(&self, a: i32, b: i32) -> i32 { self.strategy.execute(a, b) }}
// Usagefn main() { let mut calc = Calculator::new(Box::new(AddStrategy)); println!("{}", calc.calculate(5, 3)); // 8
calc.set_strategy(Box::new(MultiplyStrategy)); println!("{}", calc.calculate(5, 3)); // 15}Code Examples (PHP)
Section titled βCode Examples (PHP)β<?php
// Strategyinterface IStrategy{ public function execute(int $a, int $b): int;}
// Concrete Strategiesclass AddStrategy implements IStrategy{ public function execute(int $a, int $b): int { return $a + $b; }}
class SubtractStrategy implements IStrategy{ public function execute(int $a, int $b): int { return $a - $b; }}
class MultiplyStrategy implements IStrategy{ public function execute(int $a, int $b): int { return $a * $b; }}
// Contextclass Calculator{ private IStrategy $strategy;
public function setStrategy(IStrategy $strategy): void { $this->strategy = $strategy; }
public function calculate(int $a, int $b): int { return $this->strategy->execute($a, $b); }}
// Usage$calc = new Calculator();
$calc->setStrategy(new AddStrategy());echo $calc->calculate(5, 3) . "\n"; // 8
$calc->setStrategy(new MultiplyStrategy());echo $calc->calculate(5, 3) . "\n"; // 15Code Examples (Go)
Section titled βCode Examples (Go)βpackage main
import ( "fmt")
// Strategytype Strategy interface { Execute(a, b int) int}
// Concrete Strategiestype AddStrategy struct{}type SubtractStrategy struct{}type MultiplyStrategy struct{}
func (AddStrategy) Execute(a, b int) int { return a + b}
func (SubtractStrategy) Execute(a, b int) int { return a - b}
func (MultiplyStrategy) Execute(a, b int) int { return a * b}
// Contexttype Calculator struct { strategy Strategy}
func (c *Calculator) SetStrategy(s Strategy) { c.strategy = s}
func (c *Calculator) Calculate(a, b int) int { return c.strategy.Execute(a, b)}
// Usagefunc main() { calc := &Calculator{}
calc.SetStrategy(AddStrategy{}) fmt.Println(calc.Calculate(5, 3)) // 8
calc.SetStrategy(MultiplyStrategy{}) fmt.Println(calc.Calculate(5, 3)) // 15}Code Examples (Ruby)
Section titled βCode Examples (Ruby)β# Strategyclass Strategydef execute(a, b) raise NotImplementedError, "Subclasses must implement execute"endend
# Concrete Strategiesclass AddStrategy < Strategydef execute(a, b) a + bendend
class SubtractStrategy < Strategydef execute(a, b) a - bendend
class MultiplyStrategy < Strategydef execute(a, b) a * bendend
# Contextclass Calculatordef initialize @strategy = nilend
def set_strategy(strategy) @strategy = strategyend
def calculate(a, b) @strategy.execute(a, b)endend
# Usagecalc = Calculator.new
calc.set_strategy(AddStrategy.new)puts calc.calculate(5, 3) # 8
calc.set_strategy(MultiplyStrategy.new)puts calc.calculate(5, 3) # 15Code Examples (Perl)
Section titled βCode Examples (Perl)β#!/usr/bin/perluse strict;use warnings;
############################# Strategy (Interface-like)############################package Strategy;
sub execute { die "execute() must be implemented by subclass";}
############################# Concrete Strategies############################package AddStrategy;our @ISA = ('Strategy');
sub new { bless {}, shift }
sub execute { my ($self, $a, $b) = @_; return $a + $b;}
package SubtractStrategy;our @ISA = ('Strategy');
sub new { bless {}, shift }
sub execute { my ($self, $a, $b) = @_; return $a - $b;}
package MultiplyStrategy;our @ISA = ('Strategy');
sub new { bless {}, shift }
sub execute { my ($self, $a, $b) = @_; return $a * $b;}
############################# Context############################package Calculator;
sub new { bless { strategy => undef }, shift }
sub set_strategy { my ($self, $strategy) = @_; $self->{strategy} = $strategy;}
sub calculate { my ($self, $a, $b) = @_; return $self->{strategy}->execute($a, $b);}
############################# Usage############################package main;
my $calc = Calculator->new();
$calc->set_strategy(AddStrategy->new());print $calc->calculate(5, 3), "\n"; # 8
$calc->set_strategy(MultiplyStrategy->new());print $calc->calculate(5, 3), "\n"; # 15Code Examples (TypeScript)
Section titled βCode Examples (TypeScript)β// Strategyinterface IStrategy { execute(a: number, b: number): number;}
// Concrete Strategiesclass AddStrategy implements IStrategy { execute(a: number, b: number): number { return a + b; }}
class SubtractStrategy implements IStrategy { execute(a: number, b: number): number { return a - b; }}
class MultiplyStrategy implements IStrategy { execute(a: number, b: number): number { return a * b; }}
// Contextclass Calculator { private strategy: IStrategy;
setStrategy(strategy: IStrategy): void { this.strategy = strategy; }
calculate(a: number, b: number): number { return this.strategy.execute(a, b); }}
// Usageconst calc = new Calculator();
calc.setStrategy(new AddStrategy());console.log(calc.calculate(5, 3)); // 8
calc.setStrategy(new MultiplyStrategy());console.log(calc.calculate(5, 3)); // 15