2.2.12 · D3Design Principles

Worked examples — Design Patterns — Behavioral - Observer, Strategy, Command, Iterator, State, Template Method, Chain of Responsibility,

4,114 words19 min readBack to topic

See the parent for definitions: Behavioral Design Patterns.


The scenario matrix

Before we solve anything, let us list every case class a behavioral-pattern problem can hand you. Think of this like listing all four quadrants before doing trigonometry — we want zero surprises later. The "Covered by" column gives a one-line summary so you can grasp the whole map without flipping.

Cell Case class What makes it tricky Covered by (one-line result)
C1 Normal Observer fan-out many listeners, order of notify Ex 1 — 2 listeners print 2 lines, in subscribe order
C2 Degenerate: zero observers notify() with an empty list Ex 1 — empty list ⇒ loop runs 0 times, silent
C3 Unsubscribe during notify list mutated while looping (the classic bug) Ex 2 — raw loop skips a listener; copy the list to fix
C4 Strategy chosen at runtime swap algorithm without an if Ex 3 — same context, [1,2,3] vs [3,2,1]
C5 Strategy vs State confusion same UML, different intent Ex 4 — State self-mutates state; Strategy is inert
C6 Command undo — full & empty history undo() when nothing is stacked Ex 5 — undo returns doc to []; 3rd undo needs a guard
C7 Iterator: two independent walks state must live in the iterator Ex 6 — both walks give [3,2,1], no interference
C8 Iterator: empty collection loop body runs zero times Ex 6 — Countdown(0) yields []
C9 State machine: legal vs illegal transition ignore vs act Ex 7 — Draft→Published, 2nd publish is a no-op
C10 Template Method: fixed skeleton, filled hook subclass overrides a step, never the order Ex 8 — Sales fills body(), order stays header/body/footer
C11 Real-world + exam twist: Chain of Responsibility who handles, who passes, nobody handles Ex 9 — L1/Manager handle; level-4 falls off the end

We will now walk every cell.


Example 1 — Observer fan-out, and the empty case (C1 + C2)


Example 2 — Unsubscribe during notify (C3, the classic bug)


Example 3 — Strategy chosen at runtime (C4)


Example 4 — Strategy vs State: same shape, different soul (C5)


Example 5 — Command undo: full history and empty history (C6)


Example 6 — Iterator: two independent walks, and the empty collection (C7 + C8)



Example 8 — Template Method: fixed skeleton, filled hook (C10)


Example 9 — Chain of Responsibility: real-world word problem + the "nobody handles" twist (C11)


Recall Self-test: cover the answers

An Observer's notify loops the raw list and a listener unsubscribes itself — what breaks? ::: The listener that shifts into the just-visited slot is skipped (index shift); fix by iterating over a copy. undo() on empty history does what? ::: With the if history: guard, it's a safe no-op; without it, raises IndexError. Two for loops over one Iterator object — do they interfere? ::: No; each for calls __iter__ and gets fresh, independent state. In Template Method, what can a subclass change and what can't it? ::: It fills the hooks (individual steps) but cannot change the order — the base class owns the skeleton. A level-4 ticket in a chain that maxes at level 3? ::: Falls off the end unhandled — needs a fallback handler. Same UML for Strategy and State — what distinguishes them? ::: State objects trigger their own transitions; strategies are inert and client-chosen.