Behavioral Patterns I: Strategy & Template Method
Design Patterns
Chapter 7 · Behavioral Patterns I: Strategy & Template Method
This course's first two categories — Creational (Chapters 2–3) and Structural (Chapters 4–6) — were about creating and composing objects. Behavioral patterns, starting here, are about how objects communicate and share responsibility for an algorithm. Strategy and Template Method both let one algorithm vary — but one does it through composition, swappable at runtime; the other through inheritance, fixed at the class level.
Strategy: Swapping an Algorithm at Runtime
Strategy defines a family of interchangeable algorithms behind one shared interface, and lets the object using them be handed a different one — even after it's already been created.
Order(10, StandardShipping()) reports 5.0. Calling set_shipping_strategy(ExpressShipping()) on that exact same order object — no new Order created — changes the next calculate_shipping() call to 17.0. Swapping again to OvernightShipping() gives 40.0. All three match their hand-calculated values (10×0.5, 10×1.2+5, 10×2.5+15) exactly — the order's own weight and identity never changed; only which algorithm object it delegates to did.
Order.calculate_shipping() genuinely changes what gets computed depending on which strategy is plugged in. Chapter 6's BankAccountProxy never changed what withdraw() computed — it only decided whether the identical, unchanged computation was allowed to run at all. Strategy swaps behavior; Proxy gates access to one fixed behavior.
Template Method: Fixing the Skeleton, Letting Subclasses Fill In Steps
Template Method takes the opposite approach: the overall sequence of steps is locked in one place (the base class), and subclasses override only the specific steps that genuinely need to differ.
Tea().prepare_recipe() returns ['Boiling water', 'Steeping the tea', 'Pouring into cup', 'Adding lemon']. Coffee().prepare_recipe() returns ['Boiling water', 'Dripping coffee through filter', 'Pouring into cup', 'Adding sugar and milk']. Step 1 (boil_water) and step 3 (pour_in_cup) are confirmed identical between the two — inherited, unmodified, from CaffeineBeverage. Only step 2 (brew) and step 4 (add_condiments) — the two steps each subclass actually overrides — differ.
CaffeineBeverage().prepare_recipe() directly — with no subclass, no overrides — correctly raises NotImplementedError the moment it reaches self.brew(). The base class defines the shape of the algorithm, not a runnable one by itself; it genuinely needs a subclass to supply the missing steps.
An Optional Step: the Hook Method
A hook is a step with a sensible default in the base class that a subclass may — but doesn't have to — override, letting a subclass skip or alter part of the fixed sequence without breaking it for everyone else.
TeaWithHook doesn't override customer_wants_condiments() — its prepare_recipe() returns all 4 steps, including 'Adding lemon'. CoffeeWithHook does override it, returning False — its prepare_recipe() correctly returns only 3 steps, with add_condiments() never even called. Both subclasses share the exact same prepare_recipe() method, inherited unmodified — the hook alone decided whether the fourth step ran.
Where This Connects
| Question | Strategy | Template Method |
|---|---|---|
| How does behavior vary? | Composition — a swappable object plugged in at runtime | Inheritance — a subclass overriding fixed steps |
| Can it change after the object is created? | Yes — verified: the same Order object gave 3 different results after 2 runtime swaps | No — a Tea object is a Tea object for its whole lifetime; changing behavior means using a different subclass |
| Where does the overall sequence live? | Nowhere fixed — the caller decides what to call and when | Fixed once, in the base class's own template method — verified identical for every subclass |
calculate_price_v2(price, strategy) — was actually an early, informal look at Strategy, before this course had named it. Chapter 9's Command pattern will look structurally similar to Strategy (an object passed in to be called later) but for a genuinely different purpose: representing a request as an object, not swapping an algorithm.
Hands-On Exercises
Add a fourth strategy, FreeShipping (always returns 0, regardless of weight), to this chapter's own Order setup. Starting from an Order(25, StandardShipping()), swap through all four strategies in turn on the same order object and verify each result.
Add a third subclass, HotChocolate, to this chapter's own CaffeineBeverage template. Verify its prepare_recipe() shares the exact same boil_water/pour_in_cup steps as Tea and Coffee, while its own brew/add_condiments steps differ from both.
Explain, using this chapter's own two verified findings, why swapping an Order's shipping strategy at runtime is possible, but "swapping" a Tea object into a Coffee at runtime is not something Template Method supports at all — what would you have to do instead?
Chapter 7 Quick Reference
- Strategy: a family of interchangeable algorithms behind one interface, held by composition — verified: the same
Orderobject produced5.0, then17.0, then40.0across two runtime strategy swaps, with no new object ever created - Template Method: a fixed algorithm skeleton in a base class, with specific steps left to subclasses — verified: two subclasses shared identical
boil_water/pour_in_cupsteps while their overriddenbrew/add_condimentssteps genuinely differed, and a hook method let one subclass skip a step entirely (3 steps vs. 4) - Next chapter: Behavioral Patterns II — Observer and State