| name | behavioral |
| description | Behavioral design patterns from the Gang of Four — Chain of Responsibility, Command, Interpreter, Iterator, Mediator, Memento, Observer, State, Strategy, Template Method, and Visitor. Patterns that manage algorithms, relationships, and responsibilities between objects.
USE FOR: object communication, event handling, state management, algorithm encapsulation, request handling chains, undo/redo, publish-subscribe
DO NOT USE FOR: object creation (use creational), structural composition (use structural)
|
| license | MIT |
| metadata | {"displayName":"Behavioral Patterns","author":"Tyler-R-Kendrick"} |
| compatibility | claude, copilot, cursor |
| references | [{"title":"Refactoring.Guru — Behavioral Design Patterns","url":"https://refactoring.guru/design-patterns/behavioral-patterns"},{"title":"Behavioral Pattern — Wikipedia","url":"https://en.wikipedia.org/wiki/Behavioral_pattern"}] |
Behavioral Design Patterns
Overview
Behavioral patterns are concerned with algorithms and the assignment of responsibilities between objects. They describe not just patterns of objects or classes but also the patterns of communication between them. These patterns characterize complex control flow that is difficult to follow at run-time — they shift your focus away from flow of control to the way objects are interconnected.
1. Chain of Responsibility
Intent
Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it.
Structure
┌──────────────────┐
│ Handler │◀────────────┐
│ (interface) │ │ next
├──────────────────┤ │
│ + handle(req) │─────────────┘
│ + setNext(h) │
└──────┬───────────┘
│ implements
├──────────────┬──────────────┐
▼ ▼ ▼
┌────────────┐ ┌────────────┐ ┌────────────┐
│ HandlerA │ │ HandlerB │ │ HandlerC │
└────────────┘ └────────────┘ └────────────┘
Participants
- Handler — defines an interface for handling requests; optionally links to a successor
- ConcreteHandler — handles requests it is responsible for; forwards unhandled requests to the next handler
- Client — initiates the request to a handler in the chain
When to Use
- More than one object may handle a request, and the handler is not known a priori
- You want to issue a request to one of several objects without specifying the receiver explicitly
- The set of handlers should be configurable dynamically (e.g., middleware pipelines)
TypeScript Example
interface SupportHandler {
setNext(handler: SupportHandler): SupportHandler;
handle(issue: { level: string; message: string }): string;
}
abstract class BaseSupportHandler implements SupportHandler {
private next: SupportHandler | null = null;
setNext(handler: SupportHandler): SupportHandler {
this.next = handler;
return handler;
}
handle(issue: { level: string; message: string }): string {
if (this.next) {
return this.next.handle(issue);
}
return `No handler found for: ${issue.message}`;
}
}
class {
(: { : ; : }): {
(issue. === ) {
;
}
.(issue);
}
}
{
(: { : ; : }): {
(issue. === ) {
;
}
.(issue);
}
}
{
(: { : ; : }): {
(issue. === ) {
;
}
.(issue);
}
}
tier1 = ();
tier2 = ();
engineering = ();
tier1.(tier2).(engineering);
.(tier1.({ : , : }));
.(tier1.({ : , : }));
2. Command
Intent
Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations.
Structure
┌──────────┐ ┌──────────────────┐ ┌──────────────┐
│ Invoker │──────▶│ Command │ │ Receiver │
│ │ │ (interface) │ ├──────────────┤
└──────────┘ ├──────────────────┤ │ + action() │
│ + execute() │ └──────┬───────┘
│ + undo() │ │
└──────┬───────────┘ │
│ implements │
▼ │
┌──────────────────┐ │
│ ConcreteCommand │──────────────┘
├──────────────────┤ calls
│ - receiver │
│ - state │
│ + execute() │
│ + undo() │
└──────────────────┘
Participants
- Command — declares an interface for executing an operation
- ConcreteCommand — binds a Receiver to an action; implements execute/undo
- Invoker — asks the command to carry out the request
- Receiver — knows how to perform the operations associated with the request
When to Use
- You need to parameterize objects with an action to perform
- You need to specify, queue, and execute requests at different times
- You need to support undo/redo
- You need to support logging changes so they can be reapplied after a crash
TypeScript Example
class TextEditor {
content = "";
insert(text: string, position: number): void {
this.content = this.content.slice(0, position) + text + this.content.slice(position);
}
delete(position: number, length: number): string {
const deleted = this.content.slice(position, position + length);
this.content = this.content.slice(0, position) + this.content.slice(position + length);
return deleted;
}
}
interface EditorCommand {
execute(): void;
undo(): void;
}
class InsertCommand implements EditorCommand {
() {}
(): {
..(., .);
}
(): {
..(., ..);
}
}
{
deletedText = ;
() {}
(): {
. = ..(., .);
}
(): {
..(., .);
}
}
{
: [] = [];
: [] = [];
(: ): {
command.();
..(command);
. = [];
}
(): {
cmd = ..();
(cmd) {
cmd.();
..(cmd);
}
}
(): {
cmd = ..();
(cmd) {
cmd.();
..(cmd);
}
}
}
editor = ();
history = ();
history.( (editor, , ));
history.( (editor, , ));
.(editor.);
history.();
.(editor.);
history.();
.(editor.);
3. Iterator
Intent
Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation.
Structure
┌──────────────────┐ ┌──────────────────┐
│ Aggregate │───────▶│ Iterator │
│ (interface) │creates │ (interface) │
├──────────────────┤ ├──────────────────┤
│ + createIterator()│ │ + hasNext(): bool │
└──────────────────┘ │ + next(): T │
└──────────────────┘
Participants
- Iterator — defines an interface for accessing and traversing elements
- ConcreteIterator — implements the Iterator interface; tracks the current position
- Aggregate — defines an interface for creating an Iterator object
- ConcreteAggregate — returns an instance of the ConcreteIterator
When to Use
- You want to access a collection's elements without exposing its internal structure
- You want to support multiple traversals of the same collection
- You want a uniform interface for traversing different types of collections
TypeScript Example
interface Iterator<T> {
hasNext(): boolean;
next(): T;
reset(): void;
}
class TreeNode<T> {
children: TreeNode<T>[] = [];
constructor(public value: T) {}
add(...nodes: TreeNode<T>[]): this {
this.children.push(...nodes);
return this;
}
}
class DepthFirstIterator<T> implements Iterator<T> {
private stack: TreeNode<T>[];
constructor(private root: TreeNode<T>) {
this.stack = [root];
}
hasNext(): boolean {
return this.stack.length > 0;
}
next(): T {
if (!this.()) ();
node = ..()!;
( i = node.. - ; i >= ; i--) {
..(node.[i]);
}
node.;
}
(): {
. = [.];
}
}
<T> <T> {
: <T>[];
() {
. = [root];
}
(): {
.. > ;
}
(): T {
(!.()) ();
node = ..()!;
..(...node.);
node.;
}
(): {
. = [.];
}
}
tree = ()
.(
().( (), ()),
().( ())
);
dfs = (tree);
: [] = [];
(dfs.()) dfsResult.(dfs.());
.(, dfsResult.());
bfs = (tree);
: [] = [];
(bfs.()) bfsResult.(bfs.());
.(, bfsResult.());
4. Mediator
Intent
Define an object that encapsulates how a set of objects interact. Mediator promotes loose coupling by keeping objects from referring to each other explicitly, and it lets you vary their interaction independently.
Structure
┌──────────────────┐ ┌──────────────────┐
│ Mediator │◀───────│ Colleague │
│ (interface) │ │ (interface) │
├──────────────────┤ ├──────────────────┤
│ + notify(sender, │ │ - mediator │
│ event) │ └──────┬───────────┘
└──────┬───────────┘ │ implements
│ implements ├──────────┐
▼ ▼ ▼
┌──────────────────┐ ┌──────────┐┌──────────┐
│ ConcreteMediator │ │ColleagueA││ColleagueB│
│ │ └──────────┘└──────────┘
│ - colleagueA │
│ - colleagueB │
└──────────────────┘
Participants
- Mediator — defines an interface for communicating with Colleague objects
- ConcreteMediator — implements cooperative behavior by coordinating Colleague objects
- Colleague — each Colleague knows its Mediator and communicates with it instead of other Colleagues
When to Use
- A set of objects communicate in well-defined but complex ways
- Reusing an object is difficult because it refers to and communicates with many other objects
- A behavior distributed between several classes should be customizable without subclassing
TypeScript Example
interface ChatMediator {
sendMessage(message: string, sender: ChatUser): void;
addUser(user: ChatUser): void;
}
class ChatUser {
private messages: string[] = [];
constructor(
public name: string,
private mediator: ChatMediator
) {
mediator.addUser(this);
}
send(message: string): void {
console.log(`${this.name} sends: ${message}`);
this.mediator.sendMessage(message, this);
}
receive(message: string, from: string): void {
const formatted = `${from}: `;
..(formatted);
.();
}
(): [] {
[....];
}
}
{
: [] = [];
(: ): {
..(user);
}
(: , : ): {
( user .) {
(user !== sender) {
user.(message, sender.);
}
}
}
}
room = ();
alice = (, room);
bob = (, room);
charlie = (, room);
alice.();
bob.();
5. Memento
Intent
Without violating encapsulation, capture and externalize an object's internal state so that the object can be restored to this state later.
Structure
┌──────────────┐ ┌──────────────────┐ ┌──────────────┐
│ Caretaker │────▶│ Memento │◀────│ Originator │
├──────────────┤keeps│ (opaque) │saves├──────────────┤
│ - mementos[] │ ├──────────────────┤ │ - state │
└──────────────┘ │ + getState() │ │ + save() │
└──────────────────┘ │ + restore(m) │
└──────────────┘
Participants
- Memento — stores internal state of the Originator; protects against access by objects other than the Originator
- Originator — creates a Memento containing a snapshot of its current state; uses a Memento to restore
- Caretaker — responsible for keeping the Memento; never operates on or examines its contents
When to Use
- A snapshot of an object's state must be saved so it can be restored later
- A direct interface to obtaining the state would expose implementation details and break encapsulation
TypeScript Example
class EditorMemento {
constructor(
private readonly content: string,
private readonly cursorPos: number,
private readonly timestamp: Date
) {}
getContent(): string { return this.content; }
getCursorPos(): number { return this.cursorPos; }
getTimestamp(): Date { return this.timestamp; }
}
class DocumentEditor {
private content = "";
private cursorPos = 0;
type(text: string): void {
this.content =
this.content.slice(0, this.cursorPos) +
text +
this..(.);
. += text.;
}
(: ): {
. = .(, .(pos, ..));
}
(): {
(., ., ());
}
(: ): {
. = memento.();
. = memento.();
}
(): {
;
}
}
{
: [] = [];
(: ): {
..(memento);
}
(): | {
..();
}
}
doc = ();
history = ();
doc.();
history.(doc.());
doc.();
history.(doc.());
doc.();
.(doc.());
doc.(history.()!);
.(doc.());
doc.(history.()!);
.(doc.());
6. Observer
Intent
Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.
Structure
┌──────────────────┐ ┌──────────────────┐
│ Subject │────────▶│ Observer │
├──────────────────┤ notifies│ (interface) │
│ - observers[] │ ├──────────────────┤
│ + attach(obs) │ │ + update(data) │
│ + detach(obs) │ └──────┬───────────┘
│ + notify() │ │ implements
└──────────────────┘ ▼
┌──────────────────┐
│ ConcreteObserver │
├──────────────────┤
│ + update(data) │
└──────────────────┘
Participants
- Subject — knows its observers; provides an interface for attaching and detaching Observer objects
- Observer — defines an updating interface for objects that should be notified of changes
- ConcreteSubject — stores state of interest; sends a notification when state changes
- ConcreteObserver — maintains a reference to a ConcreteSubject; implements the update interface
When to Use
- When a change to one object requires changing others, and you do not know how many objects need to change
- When an object should notify other objects without making assumptions about who those objects are
TypeScript Example
interface EventListener<T> {
update(event: string, data: T): void;
}
class EventEmitter<T> {
private listeners = new Map<string, Set<EventListener<T>>>();
subscribe(event: string, listener: EventListener<T>): () => void {
if (!this.listeners.has(event)) {
this.listeners.set(event, new Set());
}
this.listeners.get(event)!.add(listener);
return () => this.listeners.get(event)?.delete(listener);
}
protected notify(event: string, data: T): void {
this..(event)?.( listener.(event, data));
}
}
<{ : ; : }> {
state = <, >();
(: , : ): {
old = ..(key);
..(key, value);
.(, { key, value });
(old === ) {
.(, { key, value });
}
}
(: ): {
..(key);
}
}
<{ : ; : }> {
(: , : { : ; : }): {
.();
}
}
<{ : ; : }> {
(: , : { : ; : }): {
(data. === && data. === ) {
(!data..()) {
.();
}
}
}
}
store = ();
unsubLog = store.(, ());
store.(, ());
store.(, );
store.(, );
();
store.(, );
7. State
Intent
Allow an object to alter its behavior when its internal state changes. The object will appear to change its class.
Structure
┌──────────────────┐ ┌──────────────────┐
│ Context │───────▶│ State │
├──────────────────┤ has-a │ (interface) │
│ - state: State │ ├──────────────────┤
│ + request() │ │ + handle(ctx) │
└──────────────────┘ └──────┬───────────┘
│ implements
┌──────┼──────────┐
▼ ▼ ▼
┌─────────┐┌─────────┐┌─────────┐
│ StateA ││ StateB ││ StateC │
└─────────┘└─────────┘└─────────┘
Participants
- Context — maintains an instance of a ConcreteState subclass that defines the current state
- State — defines an interface for encapsulating the behavior associated with a particular state
- ConcreteState — each subclass implements behavior associated with a state of the Context
When to Use
- An object's behavior depends on its state, and it must change behavior at runtime
- Operations have large multipart conditional statements that depend on the object's state
TypeScript Example
interface OrderState {
next(order: Order): void;
cancel(order: Order): void;
toString(): string;
}
class Order {
private state: OrderState;
constructor(public readonly id: string) {
this.state = new PendingState();
}
setState(state: OrderState): void {
console.log(` Order ${this.id}: ${this.state} -> ${state}`);
this.state = state;
}
next(): void { this.state.next(this); }
cancel(): void { this.state.(); }
(): { ..(); }
}
{
(: ): { order.( ()); }
(: ): { order.( ()); }
(): { ; }
}
{
(: ): { order.( ()); }
(: ): {
.();
order.( ());
}
(): { ; }
}
{
(: ): { order.( ()); }
(: ): {
.();
}
(): { ; }
}
{
(: ): {
.();
}
(: ): {
.();
}
(): { ; }
}
{
(: ): {
.();
}
(: ): {
.();
}
(): { ; }
}
order = ();
order.();
order.();
order.();
order.();
8. Strategy
Intent
Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.
Structure
┌──────────────────┐ ┌──────────────────┐
│ Context │───────▶│ Strategy │
├──────────────────┤ has-a │ (interface) │
│ - strategy │ ├──────────────────┤
│ + execute() │ │ + execute(data) │
└──────────────────┘ └──────┬───────────┘
│ implements
┌──────┼──────────┐
▼ ▼ ▼
┌────────┐┌────────┐┌────────┐
│ StratA ││ StratB ││ StratC │
└────────┘└────────┘└────────┘
Participants
- Strategy — declares an interface common to all supported algorithms
- ConcreteStrategy — implements the algorithm using the Strategy interface
- Context — is configured with a ConcreteStrategy; delegates to its Strategy
When to Use
- Many related classes differ only in their behavior
- You need different variants of an algorithm
- An algorithm uses data that clients should not know about
- A class defines many behaviors, and these appear as multiple conditional statements
TypeScript Example
interface CompressionStrategy {
compress(data: string): string;
name: string;
}
class GzipStrategy implements CompressionStrategy {
name = "gzip";
compress(data: string): string {
return `[gzip:${data.length}]${data.substring(0, 10)}...`;
}
}
class BrotliStrategy implements CompressionStrategy {
name = "brotli";
compress(data: string): string {
return `[br:${data.length}]${data.substring(0, 8)}...`;
}
}
class NoCompressionStrategy implements CompressionStrategy {
name = "none";
compress(data: string): string {
return data;
}
}
class {
() {}
(: ): {
. = strategy;
}
(: , : ): {
result = ..(content);
;
}
}
compressor = ( ());
.(compressor.(, .()));
compressor.( ());
.(compressor.(, .()));
(): {
(size > ) ();
(size > ) ();
();
}
9. Template Method
Intent
Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure.
Structure
┌──────────────────────┐
│ AbstractClass │
├──────────────────────┤
│ + templateMethod() │ ← calls step1, step2, step3 in order
│ + step1() │ ← may have default implementation
│ + step2() │ ← abstract — subclass MUST implement
│ + step3() │ ← hook — subclass CAN override
└──────┬───────────────┘
│ extends
▼
┌──────────────────────┐
│ ConcreteClass │
├──────────────────────┤
│ + step2() │ ← implements required step
│ + step3() │ ← optionally overrides hook
└──────────────────────┘
Participants
- AbstractClass — defines the template method (the skeleton); declares abstract steps and optional hooks
- ConcreteClass — implements the abstract steps; optionally overrides hooks
When to Use
- You want to implement the invariant parts of an algorithm once and let subclasses fill in the varying parts
- You want to control which parts of an algorithm subclasses can override (hooks vs. required steps)
- Common behavior among subclasses should be factored and localized in a common class to avoid duplication
TypeScript Example
abstract class DataPipeline {
run(source: string): string {
const raw = this.extract(source);
const validated = this.validate(raw);
const transformed = this.transform(validated);
this.beforeLoad(transformed);
const result = this.load(transformed);
this.afterLoad(result);
return result;
}
protected abstract extract(source: string): string[];
protected abstract transform(data: string[]): string[];
protected abstract load(data: string[]): string;
protected (: []): [] {
data.( item.(). > );
}
(: []): {}
(: ): {}
}
{
(: ): [] {
source.();
}
(: []): [] {
headers = data[].();
data.().( {
values = row.();
: <, > = {};
headers.( obj[h.()] = values[i]?.() ?? );
.(obj);
});
}
(: []): {
;
}
(: ): {
.();
}
}
pipeline = ();
csv = ;
json = pipeline.(csv);
.(json);
10. Visitor
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 on which it operates.
Structure
┌──────────────────┐ ┌──────────────────────┐
│ Visitor │ │ Element │
│ (interface) │ │ (interface) │
├──────────────────┤ ├──────────────────────┤
│ + visitA(elemA) │ │ + accept(visitor) │
│ + visitB(elemB) │ └──────┬───────────────┘
└──────┬───────────┘ │ implements
│ implements ├──────────┐
▼ ▼ ▼
┌────────────────┐ ┌──────────┐┌──────────┐
│ConcreteVisitor │ │ ElementA ││ ElementB │
├────────────────┤ ├──────────┤├──────────┤
│ + visitA(elemA) │ │ accept(v) ││ accept(v) │
│ + visitB(elemB) │ │{v.visitA} ││{v.visitB} │
└────────────────┘ └──────────┘└──────────┘
Participants
- Visitor — declares a visit operation for each class of ConcreteElement
- ConcreteVisitor — implements each visit operation
- Element — defines an accept(visitor) method
- ConcreteElement — implements accept by calling the appropriate visitor method
When to Use
- An object structure contains many classes with differing interfaces, and you want to perform operations that depend on their concrete classes
- Many distinct and unrelated operations need to be performed on objects in a structure, and you want to avoid "polluting" their classes
- The classes in the structure rarely change, but you often define new operations over the structure
TypeScript Example
interface ASTVisitor<R> {
visitNumber(node: NumberNode): R;
visitBinaryOp(node: BinaryOpNode): R;
visitUnaryOp(node: UnaryOpNode): R;
}
interface ASTNode {
accept<R>(visitor: ASTVisitor<R>): R;
}
class NumberNode implements ASTNode {
constructor(public value: number) {}
accept<R>(visitor: ASTVisitor<R>): R {
return visitor.visitNumber(this);
}
}
class BinaryOpNode implements ASTNode {
constructor(
public operator: "+" | "-" | "*" | "/",
public left: ASTNode,
public right: ASTNode
) {}
accept<R>(visitor: ASTVisitor<R>): R {
visitor.();
}
}
{
() {}
accept<R>(: <R>): R {
visitor.();
}
}
<> {
(: ): { node.; }
(: ): {
l = node..();
r = node..();
(node.) {
: l + r;
: l - r;
: l * r;
: l / r;
}
}
(: ): {
-node..();
}
}
<> {
(: ): { (node.); }
(: ): {
;
}
(: ): {
;
}
}
ast = (,
(, (), ()),
(, ())
);
.(ast.( ()));
.(ast.( ()));
11. Interpreter
Intent
Given a language, define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language.
Note on Usage
The Interpreter pattern is the least commonly used GoF pattern in modern practice. Most real-world expression evaluation is handled by parser generators, embedded scripting engines (Lua, JS), or expression libraries. Use Interpreter only for simple, well-defined grammars (e.g., query filters, configuration rules).
Structure
┌───────────────────────┐
│ AbstractExpression │
│ (interface) │
├───────────────────────┤
│ + interpret(context) │
└──────┬────────────────┘
│ implements
├──────────────────┐
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│TerminalExpression │ │NonterminalExpr. │
│(literal values) │ │(rules/operators) │
└──────────────────┘ └──────────────────┘
When to Use
- The grammar is simple and efficiency is not a critical concern
- You want to evaluate simple rule expressions, search filters, or configuration DSLs
TypeScript Example
interface InterpretContext {
variables: Record<string, boolean>;
}
interface BoolExpression {
interpret(ctx: InterpretContext): boolean;
toString(): string;
}
class Variable implements BoolExpression {
constructor(private name: string) {}
interpret(ctx: InterpretContext): boolean {
return ctx.variables[this.name] ?? false;
}
toString(): string { return this.name; }
}
class AndExpression implements BoolExpression {
constructor(private left: BoolExpression, private right: ) {}
(: ): {
..(ctx) && ..(ctx);
}
(): { ; }
}
{
() {}
(: ): {
..(ctx) || ..(ctx);
}
(): { ; }
}
{
() {}
(: ): {
!..(ctx);
}
(): { ; }
}
expr = (
(),
(
(),
( ())
)
);
.();
.(expr.({ : { : , : , : } }));
.(expr.({ : { : , : , : } }));
.(expr.({ : { : , : , : } }));
Commonly Confused Pairs
Strategy vs State
| Aspect | Strategy | State |
|---|
| Intent | Swap an algorithm from outside | Change behavior as internal state changes |
| Who decides | The client selects the strategy | The state objects trigger transitions |
| Awareness | Strategies are unaware of each other | States often know about sibling states |
| Typical trigger | Client calls setStrategy() | Internal condition triggers setState() |
| Example | Choose sort algorithm | Order status lifecycle (Pending -> Paid -> Shipped) |
Rule of thumb: If the client picks the behavior, it is Strategy. If the object changes its own behavior based on internal conditions, it is State.
Command vs Memento
| Aspect | Command | Memento |
|---|
| Intent | Encapsulate an action as an object | Capture a snapshot of state |
| What it stores | An operation + its parameters | An object's internal state |
| Undo mechanism | Reverse the operation (execute inverse) | Restore the saved state |
| Scope | One action at a time | Full state snapshot |
| Example | "Insert text at position 5" (undo = delete) | "Save entire document state" (undo = restore) |
Rule of thumb: Command stores what to do (and how to undo it). Memento stores what things looked like (so you can go back).
Observer vs Mediator
| Aspect | Observer | Mediator |
|---|
| Direction | One-to-many (subject -> observers) | Many-to-many (colleagues <-> mediator) |
| Coupling | Observers subscribe to a subject | Colleagues only know the mediator |
| Awareness | Subject does not know observer types | Mediator knows all colleague types |
| Typical use | Event notification, data binding | Complex UI interactions, chat rooms |
| Distribution | Distributed — each subject manages its list | Centralized — one mediator coordinates all |
Rule of thumb: Observer is for broadcasting events. Mediator is for coordinating complex interactions between many objects.
Full Comparison Table
| Pattern | Key Mechanism | Problem It Solves |
|---|
| Chain of Responsibility | Linked handler chain | Decouple sender from receiver; multiple potential handlers |
| Command | Request as object | Parameterize, queue, log, undo operations |
| Interpreter | Grammar tree evaluation | Evaluate simple expressions and DSLs |
| Iterator | Sequential traversal interface | Access elements without exposing internals |
| Mediator | Central coordinator | Reduce N:M coupling between collaborating objects |
| Memento | State snapshot | Save/restore state without breaking encapsulation |
| Observer | Publish-subscribe | Notify dependents of state changes automatically |
| State | State-driven delegation | Change object behavior when state changes |
| Strategy | Algorithm delegation | Swap algorithms at runtime |
| Template Method | Skeleton + hooks | Reuse algorithm structure, vary individual steps |
| Visitor | Double dispatch | Add operations to class hierarchies without modifying them |
Decision Guide
How do objects communicate or behave?
│
├─ Pass request along until someone handles it?
│ └──▶ Chain of Responsibility
│
├─ Encapsulate a request for undo/redo/queuing?
│ └──▶ Command
│
├─ Traverse a collection without exposing internals?
│ └──▶ Iterator
│
├─ Reduce coupling between many interacting objects?
│ └──▶ Mediator
│
├─ Save and restore object state?
│ └──▶ Memento
│
├─ Notify others when state changes?
│ └──▶ Observer
│
├─ Change behavior based on internal state?
│ └──▶ State
│
├─ Swap algorithms at runtime?
│ └──▶ Strategy
│
├─ Reuse algorithm skeleton, vary steps?
│ └──▶ Template Method
│
├─ Add operations across a class hierarchy?
│ └──▶ Visitor
│
└─ Evaluate simple grammar or expressions?
└──▶ Interpreter (but consider a parser library first)