- 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
```typescript
// Handler interface
interface SupportHandler {
setNext(handler: SupportHandler): SupportHandler;
handle(issue: { level: string; message: string }): string;
}
// Base handler
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}`;
}
}
// Concrete handlers
class TierOneSupport extends BaseSupportHandler {
handle(issue: { level: string; message: string }): string {
if (issue.level === "basic") {
return `[Tier 1] Resolved: ${issue.message}`;
}
return super.handle(issue);
}
}
class TierTwoSupport extends BaseSupportHandler {
handle(issue: { level: string; message: string }): string {
if (issue.level === "intermediate") {
return `[Tier 2] Resolved: ${issue.message}`;
}
return super.handle(issue);
}
}
class EngineeringSupport extends BaseSupportHandler {
handle(issue: { level: string; message: string }): string {
if (issue.level === "critical") {
return `[Engineering] Resolved: ${issue.message}`;
}
return super.handle(issue);
}
}
// Usage — build the chain
const tier1 = new TierOneSupport();
const tier2 = new TierTwoSupport();
const engineering = new EngineeringSupport();
tier1.setNext(tier2).setNext(engineering);
console.log(tier1.handle({ level: "basic", message: "Password reset" }));
// [Tier 1] Resolved: Password reset
console.log(tier1.handle({ level: "critical", message: "Data corruption" }));
// [Engineering] Resolved: Data corruption
```
---
## 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
```typescript
// Receiver
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;
}
}
// Command interface
interface EditorCommand {
execute(): void;
undo(): void;
}
// Concrete commands
class InsertCommand implements EditorCommand {
constructor(
private editor: TextEditor,
private text: string,
private position: number
) {}
execute(): void {
this.editor.insert(this.text, this.position);
}
undo(): void {
this.editor.delete(this.position, this.text.length);
}
}
class DeleteCommand implements EditorCommand {
private deletedText = "";
constructor(
private editor: TextEditor,
private position: number,
private length: number
) {}
execute(): void {
this.deletedText = this.editor.delete(this.position, this.length);
}
undo(): void {
this.editor.insert(this.deletedText, this.position);
}
}
// Invoker with undo/redo
class CommandHistory {
private undoStack: EditorCommand[] = [];
private redoStack: EditorCommand[] = [];
execute(command: EditorCommand): void {
command.execute();
this.undoStack.push(command);
this.redoStack = []; // clear redo on new action
}
undo(): void {
const cmd = this.undoStack.pop();
if (cmd) {
cmd.undo();
this.redoStack.push(cmd);
}
}
redo(): void {
const cmd = this.redoStack.pop();
if (cmd) {
cmd.execute();
this.undoStack.push(cmd);
}
}
}
// Usage
const editor = new TextEditor();
const history = new CommandHistory();
history.execute(new InsertCommand(editor, "Hello", 0));
history.execute(new InsertCommand(editor, " World", 5));
console.log(editor.content); // "Hello World"
history.undo();
console.log(editor.content); // "Hello"
history.redo();
console.log(editor.content); // "Hello World"
```
---
## 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
```typescript
// Iterator interface
interface Iterator<T> {
hasNext(): boolean;
next(): T;
reset(): void;
}
// Concrete collection with custom iterator
class TreeNode<T> {
children: TreeNode<T>[] = [];
constructor(public value: T) {}
add(...nodes: TreeNode<T>[]): this {
this.children.push(...nodes);
return this;
}
}
// Depth-first iterator
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.hasNext()) throw new Error("No more elements");
const node = this.stack.pop()!;
// Push children in reverse so left child is processed first
for (let i = node.children.length - 1; i >= 0; i--) {
this.stack.push(node.children[i]);
}
return node.value;
}
reset(): void {
this.stack = [this.root];
}
}
// Breadth-first iterator
class BreadthFirstIterator<T> implements Iterator<T> {
private queue: TreeNode<T>[];
constructor(private root: TreeNode<T>) {
this.queue = [root];
}
hasNext(): boolean {
return this.queue.length > 0;
}
next(): T {
if (!this.hasNext()) throw new Error("No more elements");
const node = this.queue.shift()!;
this.queue.push(...node.children);
return node.value;
}
reset(): void {
this.queue = [this.root];
}
}
// Usage
const tree = new TreeNode("A")
.add(
new TreeNode("B").add(new TreeNode("D"), new TreeNode("E")),
new TreeNode("C").add(new TreeNode("F"))
);
const dfs = new DepthFirstIterator(tree);
const dfsResult: string[] = [];
while (dfs.hasNext()) dfsResult.push(dfs.next());
console.log("DFS:", dfsResult.join(" -> ")); // A -> B -> D -> E -> C -> F
const bfs = new BreadthFirstIterator(tree);
const bfsResult: string[] = [];
while (bfs.hasNext()) bfsResult.push(bfs.next());
console.log("BFS:", bfsResult.join(" -> ")); // A -> B -> C -> D -> E -> F
```
---
## 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
```typescript
// Mediator interface
interface ChatMediator {
sendMessage(message: string, sender: ChatUser): void;
addUser(user: ChatUser): void;
}
// Colleague
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}: ${message}`;
this.messages.push(formatted);
console.log(` ${this.name} received <- ${formatted}`);
}
getHistory(): string[] {
return [...this.messages];
}
}
// Concrete mediator
class ChatRoom implements ChatMediator {
private users: ChatUser[] = [];
addUser(user: ChatUser): void {
this.users.push(user);
}
sendMessage(message: string, sender: ChatUser): void {
for (const user of this.users) {
if (user !== sender) {
user.receive(message, sender.name);
}
}
}
}
// Usage — users never reference each other directly
const room = new ChatRoom();
const alice = new ChatUser("Alice", room);
const bob = new ChatUser("Bob", room);
const charlie = new ChatUser("Charlie", room);
alice.send("Hello everyone!");
// Alice sends: Hello everyone!
// Bob received <- Alice: Hello everyone!
// Charlie received <- Alice: Hello everyone!
bob.send("Hey Alice!");
// Bob sends: Hey Alice!
// Alice received <- Bob: Hey Alice!
// Charlie received <- Bob: Hey Alice!
```
---
## 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) │
└──────────────┘
```
GitHub에서 보기