Behavioral patterns focus on communication between objects, defining how objects interact and distribute responsibility. They characterize complex control flow that’s difficult to follow at runtime.

Chain of Responsibility

Problem: Multiple objects may handle a request, but the handler isn’t known in advance.

When to use:

  • More than one object may handle request, handler unknown a priori
  • Want to issue request to one of several objects without specifying receiver
  • Set of objects that can handle request should be specified dynamically

Structure:

  • Handler defines interface for handling requests
  • Concrete handlers handle requests or pass to successor
  • Client initiates request

Example (TypeScript):

interface Handler {
  setNext(handler: Handler): Handler;
  handle(request: string): string | null;
}
 
abstract class AbstractHandler implements Handler {
  private nextHandler?: Handler;
 
  setNext(handler: Handler): Handler {
    this.nextHandler = handler;
    return handler;
  }
 
  handle(request: string): string | null {
    if (this.nextHandler) {
      return this.nextHandler.handle(request);
    }
    return null;
  }
}
 
class SupportLevel1 extends AbstractHandler {
  handle(request: string): string | null {
    if (request === 'password-reset') {
      return 'L1: Password reset email sent';
    }
    return super.handle(request);
  }
}
 
class SupportLevel2 extends AbstractHandler {
  handle(request: string): string | null {
    if (request === 'account-locked') {
      return 'L2: Account unlocked';
    }
    return super.handle(request);
  }
}
 
class SupportLevel3 extends AbstractHandler {
  handle(request: string): string | null {
    if (request === 'data-corruption') {
      return 'L3: Data restored from backup';
    }
    return super.handle(request);
  }
}
 
// Usage
const l1 = new SupportLevel1();
const l2 = new SupportLevel2();
const l3 = new SupportLevel3();
 
l1.setNext(l2).setNext(l3);
 
console.log(l1.handle('password-reset'));  // L1 handles
console.log(l1.handle('account-locked'));  // L2 handles
console.log(l1.handle('data-corruption')); // L3 handles

Example (Ruby):

class Handler
  attr_writer :next_handler
 
  def handle(request)
    return @next_handler.handle(request) if @next_handler
    nil
  end
end
 
class SupportLevel1 < Handler
  def handle(request)
    return 'L1: Password reset email sent' if request == 'password-reset'
    super
  end
end
 
class SupportLevel2 < Handler
  def handle(request)
    return 'L2: Account unlocked' if request == 'account-locked'
    super
  end
end
 
# Usage
l1 = SupportLevel1.new
l2 = SupportLevel2.new
l1.next_handler = l2
 
puts l1.handle('password-reset')
puts l1.handle('account-locked')

Command

Problem: Need to encapsulate requests as objects, allowing parameterization and queuing.

When to use:

  • Parameterize objects with operations
  • Specify, queue, and execute requests at different times
  • Support undo/redo
  • Support logging changes
  • Structure system around high-level operations

Structure:

  • Command declares interface for executing operations
  • Concrete command implements execute by invoking operations on receiver
  • Invoker asks command to carry out request
  • Receiver knows how to perform operations

Example (TypeScript):

// Receiver
class Light {
  on() { console.log('Light is ON'); }
  off() { console.log('Light is OFF'); }
}
 
// Command interface
interface Command {
  execute(): void;
  undo(): void;
}
 
// Concrete commands
class LightOnCommand implements Command {
  constructor(private light: Light) {}
 
  execute() {
    this.light.on();
  }
 
  undo() {
    this.light.off();
  }
}
 
class LightOffCommand implements Command {
  constructor(private light: Light) {}
 
  execute() {
    this.light.off();
  }
 
  undo() {
    this.light.on();
  }
}
 
// Invoker
class RemoteControl {
  private history: Command[] = [];
 
  submit(command: Command) {
    command.execute();
    this.history.push(command);
  }
 
  undo() {
    const command = this.history.pop();
    if (command) {
      command.undo();
    }
  }
}
 
// Usage
const light = new Light();
const remote = new RemoteControl();
 
remote.submit(new LightOnCommand(light));
remote.submit(new LightOffCommand(light));
remote.undo(); // Turns light back on

Example (Ruby):

class Light
  def on
    puts 'Light is ON'
  end
 
  def off
    puts 'Light is OFF'
  end
end
 
class Command
  def execute
    raise NotImplementedError
  end
 
  def undo
    raise NotImplementedError
  end
end
 
class LightOnCommand < Command
  def initialize(light)
    @light = light
  end
 
  def execute
    @light.on
  end
 
  def undo
    @light.off
  end
end
 
class RemoteControl
  def initialize
    @history = []
  end
 
  def submit(command)
    command.execute
    @history << command
  end
 
  def undo
    command = @history.pop
    command&.undo
  end
end

Iterator

Problem: Need to access elements of a collection sequentially without exposing representation.

When to use:

  • Access aggregate object’s contents without exposing representation
  • Support multiple traversals of aggregate objects
  • Provide uniform interface for traversing different structures

Note: Most modern languages have built-in iterators. This pattern is more relevant when creating custom collection types.

Example (TypeScript):

interface Iterator<T> {
  current(): T;
  next(): T;
  hasNext(): boolean;
}
 
interface Aggregator<T> {
  createIterator(): Iterator<T>;
}
 
class BookIterator implements Iterator<string> {
  private position: number = 0;
 
  constructor(private books: string[]) {}
 
  current(): string {
    return this.books[this.position];
  }
 
  next(): string {
    return this.books[++this.position];
  }
 
  hasNext(): boolean {
    return this.position < this.books.length - 1;
  }
}
 
class BookCollection implements Aggregator<string> {
  private books: string[] = [];
 
  addBook(book: string) {
    this.books.push(book);
  }
 
  createIterator(): Iterator<string> {
    return new BookIterator(this.books);
  }
}
 
// Usage
const collection = new BookCollection();
collection.addBook('Design Patterns');
collection.addBook('Clean Code');
collection.addBook('Refactoring');
 
const iterator = collection.createIterator();
while (iterator.hasNext()) {
  console.log(iterator.current());
  iterator.next();
}
console.log(iterator.current()); // Last book

Mediator

Problem: Objects communicate in complex ways; need to decouple their interactions.

When to use:

  • Set of objects communicate in well-defined but complex ways
  • Reusing object is difficult because it refers to many others
  • Behavior distributed between several classes should be customizable

Structure:

  • Mediator defines interface for communicating with colleagues
  • Concrete mediator coordinates colleagues
  • Colleagues communicate through mediator

Example (TypeScript):

interface Mediator {
  notify(sender: Component, event: string): void;
}
 
class Component {
  constructor(protected mediator: Mediator) {}
}
 
class Button extends Component {
  click() {
    console.log('Button clicked');
    this.mediator.notify(this, 'click');
  }
}
 
class TextBox extends Component {
  setText(text: string) {
    console.log(`TextBox: ${text}`);
  }
}
 
class Checkbox extends Component {
  check() {
    console.log('Checkbox checked');
    this.mediator.notify(this, 'check');
  }
}
 
class Dialog implements Mediator {
  constructor(
    private button: Button,
    private textBox: TextBox,
    private checkbox: Checkbox
  ) {}
 
  notify(sender: Component, event: string) {
    if (sender === this.button && event === 'click') {
      this.textBox.setText('Button was clicked');
    }
    if (sender === this.checkbox && event === 'check') {
      this.textBox.setText('Checkbox was checked');
    }
  }
}
 
// Usage
const button = new Button(null as any);
const textBox = new TextBox(null as any);
const checkbox = new Checkbox(null as any);
 
const dialog = new Dialog(button, textBox, checkbox);
(button as any).mediator = dialog;
(textBox as any).mediator = dialog;
(checkbox as any).mediator = dialog;
 
button.click();
checkbox.check();

Memento

Problem: Need to capture and restore an object’s internal state without violating encapsulation.

When to use:

  • Snapshot of object’s state must be saved for later restoration
  • Direct interface to obtain state would expose implementation

Structure:

  • Memento stores internal state of originator
  • Originator creates memento and uses it to restore state
  • Caretaker keeps mementos but never operates on them

Example (TypeScript):

// Memento
class EditorMemento {
  constructor(private readonly content: string) {}
 
  getContent(): string {
    return this.content;
  }
}
 
// Originator
class Editor {
  private content: string = '';
 
  type(words: string) {
    this.content += words;
  }
 
  getContent(): string {
    return this.content;
  }
 
  save(): EditorMemento {
    return new EditorMemento(this.content);
  }
 
  restore(memento: EditorMemento) {
    this.content = memento.getContent();
  }
}
 
// Caretaker
class History {
  private mementos: EditorMemento[] = [];
 
  push(memento: EditorMemento) {
    this.mementos.push(memento);
  }
 
  pop(): EditorMemento | undefined {
    return this.mementos.pop();
  }
}
 
// Usage
const editor = new Editor();
const history = new History();
 
editor.type('Hello ');
history.push(editor.save());
 
editor.type('World!');
console.log(editor.getContent()); // "Hello World!"
 
editor.restore(history.pop()!);
console.log(editor.getContent()); // "Hello "

Observer

Problem: When one object changes state, all dependents need to be notified automatically.

When to use:

  • Abstraction has two aspects, one dependent on the other
  • Change to one object requires changing others
  • Object should notify other objects without knowing who they are

Structure:

  • Subject maintains list of observers and notifies them
  • Observer defines updating interface
  • Concrete observers implement update to sync with subject

Example (TypeScript):

interface Observer {
  update(subject: Subject): void;
}
 
class Subject {
  private observers: Observer[] = [];
  private state: number = 0;
 
  attach(observer: Observer) {
    this.observers.push(observer);
  }
 
  detach(observer: Observer) {
    const index = this.observers.indexOf(observer);
    if (index > -1) {
      this.observers.splice(index, 1);
    }
  }
 
  notify() {
    this.observers.forEach(observer => observer.update(this));
  }
 
  getState(): number {
    return this.state;
  }
 
  setState(state: number) {
    this.state = state;
    this.notify();
  }
}
 
class ConcreteObserver implements Observer {
  constructor(private name: string) {}
 
  update(subject: Subject) {
    console.log(`${this.name} notified. New state: ${subject.getState()}`);
  }
}
 
// Usage
const subject = new Subject();
const observer1 = new ConcreteObserver('Observer 1');
const observer2 = new ConcreteObserver('Observer 2');
 
subject.attach(observer1);
subject.attach(observer2);
 
subject.setState(10); // Both observers notified
subject.setState(20); // Both observers notified

Resources:

State

Problem: Object behavior changes based on its internal state.

When to use:

  • Object behavior depends on state and must change at runtime
  • Operations have large conditional statements that depend on state

Structure:

  • Context maintains instance of concrete state subclass
  • State defines interface for encapsulating behavior
  • Concrete states implement behavior for particular state

Example (TypeScript):

interface State {
  handle(context: Context): void;
}
 
class Context {
  private state: State;
 
  constructor(initialState: State) {
    this.state = initialState;
  }
 
  setState(state: State) {
    this.state = state;
  }
 
  request() {
    this.state.handle(this);
  }
}
 
class ConcreteStateA implements State {
  handle(context: Context) {
    console.log('State A handling request');
    context.setState(new ConcreteStateB());
  }
}
 
class ConcreteStateB implements State {
  handle(context: Context) {
    console.log('State B handling request');
    context.setState(new ConcreteStateA());
  }
}
 
// Usage
const context = new Context(new ConcreteStateA());
context.request(); // State A
context.request(); // State B
context.request(); // State A

Real-world example (Document states):

interface DocumentState {
  publish(doc: Document): void;
  render(): string;
}
 
class Document {
  private state: DocumentState;
 
  constructor() {
    this.state = new DraftState();
  }
 
  setState(state: DocumentState) {
    this.state = state;
  }
 
  publish() {
    this.state.publish(this);
  }
 
  render(): string {
    return this.state.render();
  }
}
 
class DraftState implements DocumentState {
  publish(doc: Document) {
    console.log('Publishing draft...');
    doc.setState(new ModerationState());
  }
 
  render(): string {
    return 'Draft (not visible to public)';
  }
}
 
class ModerationState implements DocumentState {
  publish(doc: Document) {
    console.log('Approving document...');
    doc.setState(new PublishedState());
  }
 
  render(): string {
    return 'In moderation (visible to moderators)';
  }
}
 
class PublishedState implements DocumentState {
  publish(doc: Document) {
    console.log('Document already published');
  }
 
  render(): string {
    return 'Published (visible to everyone)';
  }
}
 
// Usage
const doc = new Document();
console.log(doc.render()); // Draft
doc.publish();
console.log(doc.render()); // In moderation
doc.publish();
console.log(doc.render()); // Published

Strategy

Problem: Need to encapsulate interchangeable algorithms and select at runtime.

When to use:

  • Many related classes differ only in behavior
  • Need different variants of algorithm
  • Algorithm uses data clients shouldn’t know about
  • Class defines many behaviors appearing as conditionals

Structure:

  • Strategy defines common interface for algorithms
  • Concrete strategies implement different algorithms
  • Context uses strategy interface

Example (TypeScript):

interface PaymentStrategy {
  pay(amount: number): void;
}
 
class CreditCardStrategy implements PaymentStrategy {
  constructor(
    private cardNumber: string,
    private cvv: string
  ) {}
 
  pay(amount: number) {
    console.log(`Paid $${amount} with credit card ${this.cardNumber}`);
  }
}
 
class PayPalStrategy implements PaymentStrategy {
  constructor(private email: string) {}
 
  pay(amount: number) {
    console.log(`Paid $${amount} via PayPal to ${this.email}`);
  }
}
 
class CryptoStrategy implements PaymentStrategy {
  constructor(private walletAddress: string) {}
 
  pay(amount: number) {
    console.log(`Paid $${amount} in crypto to ${this.walletAddress}`);
  }
}
 
class ShoppingCart {
  private items: number[] = [];
 
  constructor(private paymentStrategy: PaymentStrategy) {}
 
  addItem(price: number) {
    this.items.push(price);
  }
 
  checkout() {
    const total = this.items.reduce((sum, price) => sum + price, 0);
    this.paymentStrategy.pay(total);
  }
 
  setPaymentStrategy(strategy: PaymentStrategy) {
    this.paymentStrategy = strategy;
  }
}
 
// Usage
const cart = new ShoppingCart(new CreditCardStrategy('1234-5678', '123'));
cart.addItem(100);
cart.addItem(50);
cart.checkout();
 
cart.setPaymentStrategy(new PayPalStrategy('user@example.com'));
cart.checkout();

Resources:

Related: Composition over Inheritance

Template Method

Problem: Define algorithm skeleton, letting subclasses override specific steps.

When to use:

  • Implement invariant parts of algorithm once, let subclasses vary behavior
  • Control subclass extensions (hooks)
  • Factor common behavior among subclasses

Structure:

  • Abstract class defines template method and primitive operations
  • Concrete classes implement primitive operations

Example (TypeScript):

abstract class DataMiner {
  // Template method
  mine(path: string) {
    const file = this.openFile(path);
    const data = this.extractData(file);
    const parsed = this.parseData(data);
    const analyzed = this.analyzeData(parsed);
    this.sendReport(analyzed);
    this.closeFile(file);
  }
 
  // Concrete operations (same for all subclasses)
  openFile(path: string): File {
    console.log(`Opening file: ${path}`);
    return { path } as File;
  }
 
  closeFile(file: File) {
    console.log(`Closing file: ${file.path}`);
  }
 
  // Abstract operations (must be implemented by subclasses)
  abstract extractData(file: File): string;
  abstract parseData(data: string): any;
 
  // Hook (can be overridden)
  analyzeData(data: any): any {
    console.log('Default analysis');
    return data;
  }
 
  sendReport(data: any) {
    console.log('Sending report...');
  }
}
 
class PDFDataMiner extends DataMiner {
  extractData(file: File): string {
    console.log('Extracting data from PDF');
    return 'pdf data';
  }
 
  parseData(data: string): any {
    console.log('Parsing PDF data');
    return { type: 'pdf', content: data };
  }
}
 
class CSVDataMiner extends DataMiner {
  extractData(file: File): string {
    console.log('Extracting data from CSV');
    return 'csv data';
  }
 
  parseData(data: string): any {
    console.log('Parsing CSV data');
    return { type: 'csv', content: data };
  }
 
  analyzeData(data: any): any {
    console.log('Custom CSV analysis');
    return data;
  }
}
 
// Usage
const pdfMiner = new PDFDataMiner();
pdfMiner.mine('data.pdf');
 
const csvMiner = new CSVDataMiner();
csvMiner.mine('data.csv');
 
interface File { path: string; }

Visitor

Problem: Add new operations to object structure without changing classes.

When to use:

  • Object structure contains many classes with differing interfaces
  • Many distinct and unrelated operations need to be performed
  • Classes defining object structure rarely change, but operations change often

Structure:

  • Visitor declares visit operation for each concrete element
  • Concrete visitor implements operations
  • Element defines accept method taking visitor
  • Object structure can enumerate elements

Example (TypeScript):

// Visitor interface
interface ShapeVisitor {
  visitCircle(circle: Circle): void;
  visitSquare(square: Square): void;
}
 
// Element interface
interface Shape {
  accept(visitor: ShapeVisitor): void;
}
 
// Concrete elements
class Circle implements Shape {
  constructor(public radius: number) {}
 
  accept(visitor: ShapeVisitor) {
    visitor.visitCircle(this);
  }
}
 
class Square implements Shape {
  constructor(public side: number) {}
 
  accept(visitor: ShapeVisitor) {
    visitor.visitSquare(this);
  }
}
 
// Concrete visitors
class AreaCalculator implements ShapeVisitor {
  private totalArea = 0;
 
  visitCircle(circle: Circle) {
    const area = Math.PI * circle.radius * circle.radius;
    console.log(`Circle area: ${area}`);
    this.totalArea += area;
  }
 
  visitSquare(square: Square) {
    const area = square.side * square.side;
    console.log(`Square area: ${area}`);
    this.totalArea += area;
  }
 
  getTotalArea(): number {
    return this.totalArea;
  }
}
 
class PerimeterCalculator implements ShapeVisitor {
  visitCircle(circle: Circle) {
    const perimeter = 2 * Math.PI * circle.radius;
    console.log(`Circle perimeter: ${perimeter}`);
  }
 
  visitSquare(square: Square) {
    const perimeter = 4 * square.side;
    console.log(`Square perimeter: ${perimeter}`);
  }
}
 
// Usage
const shapes: Shape[] = [
  new Circle(5),
  new Square(4),
  new Circle(3)
];
 
const areaCalc = new AreaCalculator();
shapes.forEach(shape => shape.accept(areaCalc));
console.log(`Total area: ${areaCalc.getTotalArea()}`);
 
const perimCalc = new PerimeterCalculator();
shapes.forEach(shape => shape.accept(perimCalc));

See Also