2.2.12 · D5Design Principles
Question bank — Design Patterns — Behavioral - Observer, Strategy, Command, Iterator, State, Template Method, Chain of Responsibility,
True or false — justify
Observer and Publish-Subscribe are exactly the same pattern.
False. In classic Observer the subject holds a direct list of its observers and calls them itself; in Pub/Sub a broker/event-bus sits between them so publisher and subscriber never reference each other. Pub/Sub is Observer with the coupling pushed one level further out.
Strategy always requires you to write an interface and a class per algorithm.
False. The pattern requires a common calling convention, not necessarily a formal class. In Python a plain function or lambda is the strategy object —
Context(sorted) is genuine Strategy with zero extra classes.State and Strategy are the same pattern because their UML diagrams are identical.
Partly — structure yes, intent no. Both have a context delegating to an interface. But Strategy's objects are independent and chosen by the client, while State's objects know each other and trigger their own transitions. Two patterns can share a skeleton and still differ by who drives the change.
Template Method uses inheritance while Strategy uses composition.
True. Template Method overrides hook methods inside a fixed base skeleton (inheritance, decided at compile/subclass time). Strategy passes in a whole algorithm object (composition, swappable at runtime). Same goal — vary one step — opposite mechanisms.
Command turns a verb into a noun.
True. "Add text" (an action) becomes an
AddText object you can store, queue, log, or undo(). Once an action is an object it gains a lifetime beyond the instant it is called, which is what makes undo/redo and macros possible.In Chain of Responsibility every handler in the chain must process the request.
False. Each handler either handles the request or passes it along; the whole point is that most handlers decline. It is also valid for the request to reach the end unhandled — you must decide whether that is an error or a silent no-op.
Iterator lets multiple independent traversals run over the same collection at once.
True, provided traversal state lives in the iterator, not the collection. Two iterators each carry their own cursor, so one loop advancing does not disturb another. Storing the cursor on the collection would break this.
Observer's notify() should return the observers' results to the subject.
False. Observer is a push, fire-and-forget pattern: the subject broadcasts and does not wait for or care about answers. If you need answers back you are really reaching for a different pattern (Mediator or a request/response call).
Spot the error
The model class calls self.chart.update() and self.statusBar.update() directly — "it's basically Observer."
Not Observer — it's the coupling Observer removes. The model now knows the concrete UI classes and can't be tested or extended without them. Real Observer means the model holds only an abstract list and loops over an interface, never naming
chart or statusBar.A Command stores only a reference to the receiver, not the arguments, so undo() "just calls the opposite method."
Broken undo. To reverse an action you must capture enough state to invert it — often the old value or the arguments used. Without them,
undo() can't know what to restore. That captured state is why Command frequently pairs with Memento for complex reversals.A State subclass mutates self to change behavior instead of switching ctx.state.
Wrong object gets modified. The transition is reassigning the context's
state field to a new state object. Mutating the current state instead leaves the context pointing at the same class, so its behavior never actually changes.A Template Method subclass overrides generate() (the skeleton) to reorder steps.
This defeats the pattern. The skeleton method should be effectively
final; subclasses fill hook steps only, never touch the order. Overriding the skeleton means every subclass can invent its own control flow — exactly the duplication Template Method exists to prevent.A Strategy Context contains if isinstance(self.strategy, QuickSort): ....
The
if you deleted came back. The context must delegate blindly via strategy.execute() and never inspect the concrete type. Type-checking the strategy re-welds the context to specific algorithms, so adding a new strategy again means editing the context.Every handler in a Chain of Responsibility stores the full list of later handlers.
Wrong topology. A handler should know only its single
next handler (a linked list), not the whole chain. Holding the full list recreates central coupling and makes reordering or inserting handlers painful.Why questions
Why does Observer pass state (or a "changed" flag) instead of the observers pulling it?
There are two flavours: push (subject sends the data in
update(data)) and pull (subject sends just "I changed" and observers fetch what they need). Push is simple but couples the subject to what observers want; pull keeps the subject dumb but risks stale reads. The choice is a real design decision, not an accident.Why does State pass ctx into each state's method?
So the state can reassign
ctx.state, which is how a transition happens. The state must reach back into the context to swap itself out — without the reference it could decide the next state but never install it.Why keep a history list in Command instead of undoing inline?
Because commands are now first-class objects, you can stack them. A list of executed commands is literally the undo stack: pop and call
undo(). This is the backbone of any Undo-Redo Systems.Why is Template Method called "inversion of control"?
The base class owns the algorithm's order and calls down into your overridden hooks — "don't call us, we'll call you." You supply the pieces; the framework decides when they run. That is the defining feel of a framework versus a library.
Why does Strategy scale better than a growing if/elif chain?
Each new algorithm is a new object satisfying the interface — the context is never touched, honouring the Open/Closed idea from SOLID Principles. An
if/elif forces you to reopen and edit the same method for every new case, which is where bugs breed.Why do behavioral patterns lean so heavily on Polymorphism?
The recurring trick is replacing a hard-coded
if/switch or a direct call with an object you can swap. That swap only works because calling x.execute() dispatches to whichever concrete object x currently is — that runtime dispatch is polymorphism.Edge cases
What should Observer do if an observer unsubscribes during notify()?
Iterating and mutating the same list mid-loop can skip observers or crash. The standard fix is to loop over a copy of the observer list, so subscription changes take effect on the next notification, not the current one.
What happens in Chain of Responsibility when no handler accepts the request?
It falls off the end of the chain. You must deliberately choose: raise an error, log, or provide a default "sink" handler. Silently dropping requests is the classic bug — the chain "works" but nothing ever happens.
What does Iterator do on an empty collection?
has_next() is immediately false / StopIteration fires at once, so the for body simply never runs. A correct iterator needs no special-case code for empty input — emptiness is just "zero elements", the natural base case.What if a State's transition points back to itself (self-loop)?
Perfectly valid — e.g. a
Published document whose publish() says "already live" and stays Published. Not every event must change state; a no-op transition is a legitimate, explicit design choice.Can a Command's undo() itself be undone (redo)?
Yes — that is redo. Popping from the undo stack and pushing onto a redo stack lets you replay it. This only works because the command object is preserved intact; a fire-and-forget action could never be replayed.
What if two Strategies need shared state or must know results of each other?
Then Strategy is the wrong tool — its objects are meant to be independent. Coordinating collaborators that must talk to each other points toward Mediator; sequential fallback points toward Chain of Responsibility.
Recall One-line self-test before you leave
Name the single distinguishing question of each pattern. Answer ::: Observer = "who wants to know?"; Strategy = "which algorithm?"; Command = "an action as an object"; Iterator = "how to walk it?"; State = "behavior that changes with mode"; Template Method = "fixed order, variable steps"; Chain = "who handles it?"