Behavioral patterns describe how objects collaborate — who calls whom, who notifies whom, who decides what. They're how flexibility is added without inheritance, how event-driven UIs work, and how you avoid 500-line switch statements that branch on type or status.
for. The patterns survive because the names still help on a whiteboard.Intent: Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.
Sorting comparators, pricing rules, retry policies, compression algorithms, auth strategies. Same caller, different behavior plugged in.
Functional collapse: in JS, Python, Kotlin, modern Java, "Strategy" is usually just passing a function. list.sort(by: priceAscending). The OO ceremony of a Strategy interface plus N classes is overkill when a lambda fits.
Intent: Define a one-to-many dependency between objects so that when one changes state, all its dependents are notified and updated automatically.
Subjects keep a list of observers; on change, they notify each one. The basis of UI event systems, reactive frameworks (RxJS, Combine, signals), MVVM bindings, and (scaled up to a network) event-driven architectures across services.
Watch out: who owns the lifecycle? Observers that outlive the subject leak memory ("listener leak" in Android, JS DOM event handlers). Subjects that outlive observers fire into the void.
Push vs pull: push sends the new state with the notification; pull just signals "something changed" and the observer re-queries. Push is faster; pull is more flexible.
Intent: Encapsulate a request as an object, letting you parameterize clients with different requests, queue or log them, and support undoable operations.
A Command bundles target + method + arguments + (optional) undo logic into one object you can pass around. Buys you queueing, logging, retry, audit, undo/redo, and macro recording.
In the wild: CQRS' "command" comes from this idea. Job queues are commands awaiting execution. Database transaction logs and editor undo stacks are command sequences.
Intent: Allow an object to alter its behavior when its internal state changes. The object will appear to change its class.
Instead of a giant switch (status) in every method, the object delegates to a state class that gets swapped on transitions. Order in DraftState behaves differently than in SubmittedState or FulfilledState; the rules for "can cancel?" "can edit?" "can ship?" live with the state, not with the order.
Used heavily in: workflow engines, network protocols (TCP state machine), game AI, document editors, anything with non-trivial lifecycles.
Intent: Provide a way to access the elements of a collection sequentially without exposing its internal structure.
The pattern itself is a relic — every modern language builds it in as for...of, generators, IEnumerable, Iterable. But the concept — separating "how do I traverse this?" from "what is this?" — is everywhere. Streams, async iterators, paginated API clients, database cursors are all iterators in spirit.
Intent: Define the skeleton of an algorithm in a method, deferring some steps to subclasses. Subclasses redefine specific steps without changing the algorithm's structure.
Useful when several variants share a backbone but differ in details. Common in test frameworks (setUp/test/tearDown), import pipelines, scheduled-job base classes.
Composition alternative: Strategy + a small orchestrator is often more flexible — pieces can be mixed and matched at runtime. Template Method locks in inheritance.
Intent: Pass a request along a chain of handlers; each one either handles it or forwards it to the next.
Web middleware pipelines (Express, Koa, ASP.NET Core, Rack) are exactly this. Logging → auth → rate limit → router → handler. Each link decides whether to short-circuit or call next().
Used for: request middleware, exception handling chains, GUI event bubbling, document-approval workflows.
Intent: Define an object that encapsulates how a set of objects interact, so they don't have to refer to each other directly.
Reduces N² coupling between collaborators to N (each one talks only to the mediator). Air-traffic control, chat-room hubs, complex form coordination. The MediatR library in .NET is named for this pattern.
Watch out: the mediator turning into a god object that knows everything. The pattern centralizes coupling; that centralization can become its own problem.
Intent: Represent an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements.
Useful for compilers and AST transformations — adding a new pass (type-check, optimize, codegen) shouldn't require editing every node class. The Visitor "double dispatches" so the right operation runs for each node type.
Modern alternative: sealed types + pattern matching (Scala, Kotlin, modern Java, Rust) often expresses the same intent more directly. Visitor remains useful when you can't modify the element classes.
Intent: Capture and externalize an object's internal state — without breaking encapsulation — so the state can be restored later.
Undo stacks, save games, editor history, transactional rollbacks. The Memento exposes only what's needed to restore; the originator stays in control of which fields are saved.
Define a representation of a grammar plus an interpreter that evaluates sentences in it. In modern code, almost always replaced by parser generators or off-the-shelf expression engines. Worth recognizing when someone names it; rarely worth implementing from scratch.
switch on type or status. Replace with State or polymorphism (Strategy).