0 XP · 0 scenes

Strategy

Swap an algorithm without changing the code that uses it
The problem

The navigator has an if/elif for every travel mode.

The idea

Put each way into its own class behind one small interface.

Reach for it when

Several ways to do the same job (sort, pay, route, discount)

1

The picture from life

A maps app asks how you want to travel: car, bike or walking. The app and the destination are the same; only the way of working out the route and the time changes. You can switch your choice in the middle of planning.

2

The problem

What goes wrong when you do not use it.

The navigator has an if/elif for every travel mode. Each new mode (scooter, bus) means opening the navigator and growing the ladder, and the mode logic cannot be tested alone.

✗ Before
Navigatoreta(km, mode): if mode == 'car': ... elif mode == 'bike': ... elif mode == 'walk': ...
One method switches on a mode string. A new way to travel means editing it.
Reach for it when
  • Several ways to do the same job (sort, pay, route, discount)
  • You want to pick or change the way at run time
  • A big conditional chooses between behaviours
3

The idea

How the pattern fixes it.

Put each way into its own class behind one small interface. The user of the algorithm (the navigator) holds one strategy object and calls it. Swapping the object swaps the behaviour.

✓ After
Navigatorstrategyset_strategy(s)eta(km)«interface»Routeminutes(km)CarBikeWalkstrategy
  • implements
  • has a / holds
Each way to travel is its own class. Navigator holds one and can swap it any time.

Who is who

Route (strategy)
Interface: minutes(km).
Car, Bike, Walk
Concrete strategies.
Navigator (context)
Holds a strategy; eta() delegates to it; set_strategy() swaps it.

Build it in steps

  1. 1Find the part that varies.
  2. 2Define a one-method interface.
  3. 3Write each variant as a class.
  4. 4Pass the chosen one into the context.
4

Watch it run

Step through the calls. Tap an arrow to jump to it.

ClientNavigatorCarBike1eta(10)2minutes(10)3154set_strategy(Bike())5eta(10)6minutes(10)740
Step 1/7

The navigator holds a Car strategy.

eta(10)
5

The code

Python and Java. Try writing the classes yourself from the diagram first.

class Car:
    def minutes(self, km):
        return km * 60 // 40


class Bike:
    def minutes(self, km):
        return km * 60 // 15


class Walk:
    def minutes(self, km):
        return km * 60 // 5


class Navigator:
    def __init__(self, strategy):
        self.strategy = strategy

    def set_strategy(self, strategy):
        self.strategy = strategy

    def eta(self, km):
        return self.strategy.minutes(km)           # delegates, never checks the type


def demo():
    nav = Navigator(Car())
    times = [nav.eta(10)]
    nav.set_strategy(Bike())
    times.append(nav.eta(10))
    nav.set_strategy(Walk())
    times.append(nav.eta(10))
    return times

What you gain

  • Remove long conditionals
  • Test each algorithm alone
  • Swap behaviour at run time

What it costs

  • Callers must know which strategies exist to pick one
  • For one tiny difference, passing a function is lighter

You have already met it in

  • Collections.sort(list, comparator)
  • Payment methods at checkout (card, UPI, wallet)
  • Compression or hashing algorithm chosen by config

Often mixed up with

  • StateWith Strategy the client chooses and the strategies do not know each other; with State the object changes its own state.
  • Template MethodTemplate Method varies steps by inheritance; Strategy varies the whole algorithm by composition.
6

Make it stick

Write a ShippingCalculator with FlatRate, ByWeight and Express strategies. Add a fourth strategy without editing the calculator.

Confidence