Behavioral Patterns
Patterns about collaboration: who talks to whom, who owns which decision, and how behavior varies at runtime. This is the largest GoF group and the one most likely to come up when discussing a real design.
part ofSoftware design & architectureoverview, primer and where to startread it →on this pageshowhide
explore
- Behavioral Patterns Overview5 questions
- Observer6 questions
- Strategy6 questions
- Template Method5 questions
- Iterator6 questions
- Command6 questions
- State5 questions
- Chain of Responsibility6 questions
- Visitor6 questions
- Mediator5 questions
- Memento5 questions
- Interpreter6 questions
- Null Object5 questions
questions
72 · 13 sectionsIn the Gang of Four design pattern classification, what problem space do behavioral patterns address, and how do they differ from creational and structural patterns?
basics
~10 sBehavioral patterns describe how objects talk to each other and who is responsible for what. Creational patterns are about how objects get made; structural patterns are about how objects are composed into bigger shapes.
How do the Strategy and Template Method design patterns differ, and what determines which one you should reach for?
basics
~20 sBoth let part of an algorithm vary. Template Method uses inheritance: a base class fixes the steps and subclasses override some of them. Strategy uses composition: the varying algorithm is a separate object you plug in, swappable at runtime.
Explain how the State, Strategy, and Command patterns differ, even though all three delegate behaviour to a separate object.
basics
~20 sStrategy plugs in one interchangeable algorithm chosen by the caller. State makes an object behave differently as its internal state changes, with states deciding the transitions. Command wraps a request — action plus its arguments — into an object you can queue, log, or undo.
Observer, Mediator, and Chain of Responsibility all decouple a sender from a receiver. How does each one route a message differently, and when would you choose one over the others?
basics
~20 sObserver broadcasts a change to every registered listener. Mediator puts one coordinator in the middle so components talk to it instead of each other. Chain of Responsibility passes a request down a line of handlers until one handles it.
Why does the Visitor pattern need double dispatch, and what trade-off does it make that Iterator does not?
basics
~20 sVisitor lets you add new operations over a fixed set of node types without editing them. It needs two dispatches: the node picks accept(visitor), then calls visitor.visitX(this), so the right method is chosen by both the node type and the visitor type. The trade-off: adding an operation is easy, adding a node type breaks every visitor.
What problem does the Observer design pattern solve, and what are the responsibilities of the subject and the observer?
basics
~10 sObserver defines a one-to-many dependency: many observers register with one subject, and when the subject's state changes it notifies them all automatically. The subject knows only an abstract observer interface, not the concrete observers.
In the Observer pattern, what is the difference between push and pull notification, and what are the trade-offs of each?
basics
~20 sPush means the subject sends the changed data with the notification (update(newValue)). Pull means the notification only says "something changed" and each observer calls back into the subject to read what it needs. Push is efficient but guesses what observers want; pull is flexible but couples observers to the subject's query API.
What is the lapsed-listener problem in the Observer pattern, and how do you prevent it?
basics
~20 sA subject keeps a strong reference to every registered observer. If an observer is discarded without unsubscribing, the subject keeps it alive forever — it leaks memory and keeps receiving events it should no longer handle.
In an Observer-based system, what problems arise from notification ordering, reentrant updates, and update storms, and how do you control them?
basics
~20 sObservers are usually called in registration order, which is accidental, so nothing should depend on it. An observer that changes the subject during update() can trigger recursive notification or infinite loops, and one change can fan out into thousands of updates. Fixes: snapshot iteration, reentrancy guards, batching, and change coalescing.
When should an in-process Observer be replaced by publish-subscribe through a message broker, and what changes about the guarantees?
basics
~20 sUse in-process Observer when subject and observers live in the same process and must react immediately. Move to a broker when the reactors are separate services or must survive restarts. You gain durability, retries and independent scaling; you lose synchronous ordering and immediate consistency, and you must handle duplicates.
What is the Strategy design pattern, and what problem does it solve?
basics
~20 sStrategy defines a family of interchangeable algorithms, puts each one behind the same interface, and lets the calling code pick which one to use at runtime — so you swap behavior instead of editing a big if/else.
The Strategy and State patterns have nearly identical class diagrams — a context delegating to an interface with several implementations. How do they actually differ, and what does that difference change in the code?
basics
~20 sSame structure, different intent. In Strategy the client picks one algorithm and it usually stays fixed; the variants don't know about each other. In State the object's own behavior changes as its lifecycle advances, and states typically trigger the transitions to the next state.
How would you refactor a long conditional that dispatches among several algorithms into the Strategy pattern, and where does the runtime selection of the strategy end up living?
basics
~20 sExtract each branch into its own object implementing one shared interface, replace the branching call site with a delegation, and move the choice into one place — a factory or a map from key to strategy — that the client uses to pick the right implementation.
In a language with first-class functions, when is a plain function or lambda a sufficient implementation of the Strategy pattern, and when do you still want a named interface with classes?
basics
~20 sIf the strategy is one operation with no state and no extra methods, a function value is the whole pattern — a lambda is an object with one method. Prefer a named interface or class when the strategy needs configuration, identity, a name for DI/registry lookup, or more than one operation.
Compare Strategy with Template Method and with Bridge: what does each vary, by what mechanism, and how do you choose between them?
basics
~20 sStrategy swaps a whole algorithm via composition, chosen at runtime. Template Method fixes the algorithm's skeleton in a base class and lets subclasses override individual steps, bound at compile time by inheritance. Bridge splits an abstraction hierarchy from an implementation hierarchy so both can grow independently.
What is the Template Method design pattern, and what problem does it solve?
basics
~20 sA base type defines one method holding the fixed steps of an algorithm, in a fixed order. Some of those steps are left unimplemented, and subtypes fill them in. The overall shape stays the same for every subtype.
How does the Template Method pattern differ from the Strategy pattern, and how do you choose between them?
basics
~20 sBoth let part of an algorithm vary. Template Method varies it by inheritance: a subclass overrides a step inside a fixed base-class algorithm. Strategy varies it by composition: you hand the object a separate step-object, which can be swapped at runtime.
When implementing Template Method, how do you decide which steps are abstract primitive operations versus hooks with defaults, and how do you stop subclasses from breaking the algorithm's skeleton?
basics
~20 sMake a step abstract when the algorithm cannot run without it, so every subclass is forced to supply it. Make it a hook with a default when it is optional extra behaviour. Keep the main method non-overridable and the steps narrowly visible so no subclass can reorder or skip anything.
What are the main drawbacks and failure modes of the Template Method pattern, and how would you refactor away from it when they start to hurt?
basics
~20 sIt relies on inheritance, so the variant is fixed when the object is created, only one axis can vary, and subclasses depend on the base class's internals — a base-class change can break them. When it hurts, move the varying steps into injected objects or functions (Strategy) instead of subclasses.
You own a framework whose base class exposes a template method with protected extension points implemented by thousands of subclasses you don't control. How do you evolve that algorithm without breaking them?
basics
~20 sTreat every step as published API. Never add a required step or reorder existing ones; add optional hooks with defaults that keep current behaviour, deprecate slowly with clear migration notes, and offer a composition-based alternative for the next major version.
What problem does the Iterator design pattern solve, and what does "without exposing the underlying representation" mean in its intent?
basics
~20 sIterator gives a standard way to walk a collection's elements one at a time. The collection hands you a small cursor object with "is there more?" and "give me the next one" operations, so you never touch its internal array, nodes, or indexes.
What is the difference between external (pull) and internal (push) iteration, and what are the trade-offs of each?
basics
~20 sWith external iteration the caller drives the loop and pulls elements one by one (while (it.hasNext()) ...). With internal iteration the caller hands a function to the collection (forEach { ... }) and the collection runs the loop, pushing each element into that function.
What happens when a collection is structurally modified while one of its iterators is in use, and what strategies do libraries use to define that behaviour?
basics
~20 sThe cursor can be left pointing at the wrong place, so elements get skipped or repeated. Libraries pick a policy: fail fast with an error, iterate a snapshot taken when the iterator was created, or allow a "weakly consistent" walk that may or may not see later changes.
Who should own the traversal state in the Iterator pattern, and what breaks when the aggregate itself holds the cursor?
basics
~20 sThe iterator owns the position, not the collection. If the collection stores a single "current index" field, only one traversal can run at a time — nested loops, two threads, or two callers stepping through the same collection will fight over that one position.
How does the Iterator pattern change when the sequence is resource-backed or remote — a database cursor, a file, or a paginated HTTP API — rather than an in-memory collection?
basics
~20 sThe loop looks identical, but each next() may do real I/O. So the iterator now owns a resource that must be closed, can fail halfway through, is usually single-pass, and hides per-element cost — a plain-looking loop can issue thousands of network or disk requests.
What is the Command design pattern, and what are the responsibilities of the invoker, the command object, and the receiver?
basics
~20 sCommand turns a request into an object. The command holds what to do plus the data needed. The invoker (button, queue, scheduler) just calls execute() without knowing the receiver — the object that actually performs the work.
How do you implement undo and redo using the Command pattern, and what state must each command object capture to be reversible?
basics
~20 sAdd an undo() method next to execute(). Each executed command is pushed onto an undo stack. Undo pops it, calls undo(), and pushes it onto a redo stack. The command must remember whatever it needs to restore the previous state.
How would you model a single user action that must perform several operations atomically as command objects, and what happens if one of them fails partway through?
basics
~20 sWrap the child commands in one macro (composite) command that implements the same interface. Its execute() runs children in order; if one fails, it undoes the already-executed children in reverse order and rethrows, so the history sees one entry that either fully applied or not at all.
In a language with first-class functions, a request can be captured as a closure. When is a full Command object still the better choice?
basics
~20 sA closure captures a call to run later, which covers the simple case. Prefer a command object when you need more than one operation on it (undo, describe, merge), or when the request must be serialized, inspected, logged, or persisted — a closure is an opaque blob.
Reifying a request as a command object enables queueing, scheduling, retrying and logging. What properties must those command objects have to be safely queued and replayed?
basics
~20 sThe command must be self-contained and serializable — all inputs captured, no live references — and its effect must be safe to apply more than once, because queues normally deliver at-least-once. It also needs a version so old stored commands still parse.
What problem does the State design pattern solve, and how does it restructure code that would otherwise branch on a status field inside many methods?
basics
~20 sIt lets one object behave differently as its internal condition changes. Each condition becomes a small class holding the behavior for that situation, and the main object forwards calls to whichever one is current — so no giant if/else.
The State pattern and the Strategy pattern have nearly identical class diagrams. What actually distinguishes them, and how would you tell which one a given piece of code is using?
basics
~20 sSame shape, different purpose. With Strategy the caller chooses one interchangeable algorithm and it normally stays put. With State the object swaps its own behavior as its lifecycle advances, and the states know which state comes next.
In a State-pattern design, transitions can be decided inside the concrete state classes or centrally by the context. What are the trade-offs of each placement, and how do you keep the overall transition graph reviewable?
basics
~20 sIf states set the next state, each state is self-contained but the whole map is scattered across classes. If the context decides, the map lives in one readable place but the context grows. Either way, funnel every change through one transition method.
When would you replace polymorphic State-pattern classes with a data-driven finite state machine (a transition table or a state-machine library), and what do you gain and lose by doing so?
basics
~20 sSwitch to a table when there are many states and events and the rules matter more than the code: a table is one page you can read, diagram, and validate. You lose the ability to write rich, typed, per-state behavior easily.
How do you apply the State pattern to a long-lived business entity whose current state must be stored durably and whose transitions trigger external side effects such as sending an email or charging a card?
basics
~20 sStore a stable code for the state, not the object, and rebuild the state object when loading. Decide transitions in memory, commit the new state, then perform outside effects afterwards — retried safely so a repeat does not charge twice.
What is the Chain of Responsibility design pattern, and what coupling problem between a request's sender and its receiver does it solve?
basics
~20 sChain of Responsibility links several handler objects in a sequence. The sender passes a request to the first handler; each one either handles it or forwards it onward. The sender never knows which handler ultimately responds.
In a Chain of Responsibility, how does each handler decide whether to stop or continue, and what is the difference between a 'pure' chain (at most one handler acts) and a 'broadcast' chain (every handler may act)?
basics
~20 sEach handler tests whether the request is its business. In a pure chain the first competent handler processes it and stops; in a broadcast chain handlers may act and still forward, so several contribute. The contract must say which it is.
Handler order is load-bearing in a Chain of Responsibility. What failures does wrong ordering cause, and how do you make order explicit, testable, and safe to extend?
basics
~20 sWrong order can let a broad handler swallow requests meant for a specific one, or let a step run before its prerequisite (e.g. authorization before authentication). Fix it by owning order in one explicit, tested place instead of leaving it implicit.
HTTP middleware/filter pipelines are usually described as Chain of Responsibility, but a middleware wraps the rest of the chain and sees the response on the way back. How does that variant differ from the classic pattern, and when does the difference matter?
basics
~20 sClassic Chain of Responsibility passes the request forward until one handler takes it. Middleware instead receives a next function, so it runs code before and after the rest of the chain — enabling timing, error wrapping, and response rewriting on the way back.
A Chain of Responsibility explicitly allows a request to reach the end of the chain with nobody handling it. Why is that risky, and what are the options for handling fall-through?
basics
~20 sNothing guarantees a handler matches, so a request can be silently dropped — no result, no error, no log. Always define the end of the chain: a default handler, an explicit error, or a result type that forces the caller to deal with 'nobody handled it'.
What is the Visitor design pattern, and what problem does it solve?
basics
~20 sVisitor puts an operation over a set of object types into a separate 'visitor' object instead of inside those types. Each element has an accept method that calls back the right visit method. You can add new operations without editing the element classes.
What is double dispatch, and how does the Visitor pattern's accept/visit pair achieve it in a single-dispatch language?
basics
~20 sDouble dispatch means the method that runs depends on the runtime types of two objects. Most languages only dispatch on one (the receiver). Visitor chains two such calls: element.accept(v) picks by element type, then v.visit(this) picks by visitor type.
The Visitor pattern makes adding new operations easy but adding new element types hard. Explain this trade-off and how it should drive your decision to use Visitor.
basics
~20 sVisitor gathers one operation across all element types into one class, so a new operation is a new class and no edits elsewhere. But a new element type means adding a visit method to the visitor interface, which breaks every existing visitor. Use it when types are stable and operations grow.
In a Visitor-based design over a tree, who should own the traversal — the elements' accept methods, the visitor itself, or a separate iterator — and what are the consequences of each choice?
basics
~20 sTraversal can live in the element's accept (elements recurse into children), in the visitor (each visit method recurses), or in a separate walker object. Putting it in the visitor gives the most control — you can change order, skip subtrees, or stop early.
Your library publishes a Visitor interface over a node hierarchy, and downstream teams implement it. You now must add a new node type. How do you evolve the interface without breaking every implementer, and what do you lose?
basics
~20 sAdding a method to a published interface breaks everyone implementing it. Give the new visit method a default implementation (or provide an abstract base visitor with defaults) so old code still compiles — but then a visitor that never handles the new node fails silently instead of at compile time.
What is the intent of the Mediator design pattern, and what kind of coupling problem does it address?
basics
~20 sMediator defines one object that encapsulates how a group of objects interact. Instead of each object calling the others directly, they all talk to the mediator. That turns tangled many-to-many links into simple one-to-many links.
How does the Mediator pattern differ from the Observer pattern, and when would you choose one over the other?
basics
~20 sObserver broadcasts an event to subscribers the sender does not know; it carries no rules about who reacts. Mediator is a named object that knows all participants and decides what each should do. Observer is notification; Mediator is coordination.
The Mediator pattern's own well-known liability is that the mediator can become a god object. How does that happen, and what concrete techniques keep it from happening?
basics
~20 sEvery new interaction rule gets added to the one mediator, so it grows a branch per case until it knows everything and everyone edits it. Fixes: split by cohesive interaction area, keep it routing-only with logic in colleagues, and use typed handlers instead of one giant switch.
What code smells indicate that a group of collaborating classes should be refactored to introduce a Mediator, and when would introducing one be the wrong call?
basics
~20 sIntroduce a mediator when classes hold references to many siblings, one rule change forces edits in several of them, and reuse is impossible because each object hard-references the others. Skip it when only two objects talk or reactions are independent broadcasts.
Where does the Mediator idea reappear at architecture scale, and what changes about the trade-offs when the colleagues are separate services instead of objects in one process?
basics
~20 sThe same star topology appears as orchestrators, workflow/saga coordinators, message brokers, API gateways, and enterprise service buses. Across services the centralizer also becomes an availability, scaling, and ownership bottleneck — not just a code-cohesion problem.
In the Memento design pattern, what problem does it solve, and what are the responsibilities of the originator, the memento, and the caretaker?
basics
~20 sMemento saves a snapshot of an object's internal state so it can be put back later (for example, undo) without exposing that object's internals. The originator makes and restores snapshots, the memento holds the saved state, and the caretaker stores mementos without looking inside them.
The Memento pattern claims to save an object's state 'without violating encapsulation'. Concretely, how is a memento kept opaque to the caretaker while still being fully readable by the originator?
basics
~20 sThe memento offers two views: a narrow public one (basically just a handle, maybe a label or timestamp) used by the caretaker, and a wide one exposing the actual state that only the originator can reach — via a nested class, package/internal visibility, a private interface, or a language-specific friend mechanism.
Snapshot-based undo can consume unbounded memory. What concrete strategies bound the cost of retaining mementos while keeping the history useful?
basics
~20 sMemory grows as snapshot size times history depth. Bound it by capping the number of snapshots (a ring buffer), snapshotting only at meaningful boundaries instead of every change, storing deltas instead of full copies, sharing unchanged parts between snapshots, and spilling older snapshots to disk.
For implementing undo, when would you store state snapshots (Memento) versus storing reversible operations (Command), and how are the two often combined?
basics
~20 sSnapshots save the whole state and restore it wholesale — simple and always correct, but costly when the state is large. Reversible operations save just the action and how to invert it — cheap in memory, but every operation needs a correct inverse. Many systems use commands and let a command carry a small memento for the part it cannot invert.
You are designing checkpoint-and-rollback for a component whose work includes writing files, calling remote services, and holding open connections. Where does the approach of snapshotting and restoring an object's internal state stop working, and what do you do instead?
basics
~20 sSnapshots only restore state the object owns in memory. Anything that escaped — files written, messages sent, remote calls made — is not rolled back by restoring a snapshot. Those need explicit compensating actions, and live resources such as connections should be rebuilt on restore rather than captured.
What is the intent of the Interpreter design pattern, and what kind of problem is it a good fit for?
basics
~20 sInterpreter gives a small language a class per grammar rule, each with an interpret operation. A sentence is turned into a tree of these objects, and evaluating the tree executes the sentence. Best for small, stable mini-languages.
Why does the Interpreter pattern break down for large or evolving grammars, and what would you use instead?
basics
~20 sEvery grammar rule costs a class, so a big grammar means dozens of tiny classes that are hard to keep consistent, and the pattern gives you no parsing help. For real languages use a parser generator or parser combinators plus a compiled evaluator.
Walk through the participants of the Interpreter pattern — AbstractExpression, TerminalExpression, NonterminalExpression, Context, and Client — and explain what belongs in the context object.
basics
~20 sAbstractExpression declares interpret(context). Terminals are leaves like literals and variables. Non-terminals hold child expressions and combine their results. Context carries variable values and input needed while evaluating. The client builds the tree and calls interpret on the root.
When would you put the evaluation logic on the expression nodes themselves versus extracting it into a Visitor over the abstract syntax tree?
basics
~20 sPut interpret() on the nodes when the language has few operations and you expect to add new node types. Use a Visitor when you need many operations over the same tree — evaluate, print, type-check, optimize — because each becomes one class instead of a method on every node.
Where does the Interpreter pattern actually show up in real systems, and how do you tell it apart from Command, Strategy, and Composite?
basics
~20 sIt shows up wherever rules are data: search and filter expressions, feature-flag targeting, pricing and validation rules, query builders, and simple matchers. It differs from Command (one request as an object), Strategy (one swappable algorithm), and Composite (structure only, no grammar meaning).
What is the Null Object pattern in object-oriented design, and what problem does it solve?
basics
~20 sNull Object is a real object that implements an interface but does nothing (or returns harmless defaults). You hand it out instead of a null/nil reference, so calling code can just call methods without checking for absence first.
How does the Null Object pattern differ from returning an Optional/Maybe type, and when would you choose each?
basics
~20 sNull Object hides absence behind a do-nothing object so callers just call methods. Optional/Maybe shows absence in the type and forces the caller to handle it. Hide it when a harmless default exists; show it when the caller must decide.
When designing a Null Object implementation of an interface, how do you decide what each method should return, and what makes some methods impossible to implement neutrally?
basics
~20 sReturn the neutral value for how the caller uses the result: nothing for commands, 0 for sums, empty string or empty collection, false for permission checks, and another null object for object-returning methods. Methods asking about identity or existence have no neutral answer.
A team replaced null returns with do-nothing Null Object implementations across a service, and now a misconfiguration goes unnoticed in production. What went wrong, and how do you get the pattern's benefits without losing failure visibility?
basics
~20 sThey used do-nothing objects where the work was actually required, so missing configuration looked like success. Use null objects only when doing nothing is a valid outcome; elsewhere fail fast at startup, and make no-op paths visible in logs and metrics.
How does the Null Object pattern relate to Fowler's Special Case pattern, and when does a codebase outgrow a single do-nothing object?
basics
~20 sSpecial Case is the general form: named subclasses for each abnormal case (unknown, missing, suspended) with tailored behavior. Null Object is the degenerate one where the case is "absent" and the behavior is nothing. You outgrow the null object when the different absences need different behavior.