3 min read
On this page

Behavioral Design Patterns

Behavioral patterns define how objects communicate and distribute responsibilities.

Observer

Define a one-to-many dependency so that when one object changes, all dependents are notified.

INTERFACE Observer
    PROCEDURE UPDATE(event, data)

CLASS EventEmitter
    FIELDS: listeners (map of event_name -> list of Observer)

    PROCEDURE SUBSCRIBE(event, observer)
        APPEND observer TO listeners[event]

    PROCEDURE EMIT(event, data)
        IF event IN listeners
            FOR EACH obs IN listeners[event]
                obs.UPDATE(event, data)

When to use: Event systems, UI updates (MVC), pub/sub messaging, reactive programming.

Rust idiom: Channels (mpsc), callback functions, event buses.

Strategy

Define a family of algorithms, encapsulate each one, and make them interchangeable.

INTERFACE SortStrategy
    PROCEDURE SORT(data)

CLASS QuickSort IMPLEMENTS SortStrategy
    PROCEDURE SORT(data)  QUICKSORT(data)

CLASS InsertionSort IMPLEMENTS SortStrategy
    PROCEDURE SORT(data)
        FOR i ← 1 TO length(data) - 1
            j ← i
            WHILE j > 0 AND data[j-1] > data[j]
                SWAP(data[j-1], data[j]); j ← j - 1

CLASS Sorter
    FIELDS: strategy (SortStrategy)
    PROCEDURE SORT(data)  self.strategy.SORT(data)

Rust idiom: Closures often replace strategy objects:

PROCEDURE PROCESS(data, sorter_function)
    sorter_function(data)
PROCESS(data, d -> SORT(d))

When to use: Multiple algorithms for the same task. Runtime algorithm selection. Replacing complex conditionals.

Command

Encapsulate a request as an object. Enables undo, queuing, logging, and macro recording.

INTERFACE Command
    PROCEDURE EXECUTE()
    PROCEDURE UNDO()

CLASS InsertText IMPLEMENTS Command
    FIELDS: editor, text, position
    PROCEDURE EXECUTE()
        editor.INSERT(self.text, self.position)
    PROCEDURE UNDO()
        editor.DELETE(self.position, length(self.text))

CLASS CommandHistory
    FIELDS: history (list of Command)

    PROCEDURE EXECUTE(cmd)
        cmd.EXECUTE()
        APPEND cmd TO history
    PROCEDURE UNDO()
        IF history is not empty
            cmd ← POP(history)
            cmd.UNDO()

When to use: Undo/redo, transaction logging, task queues, macro recording, remote procedure calls.

Iterator

Provide a way to access elements of a collection sequentially without exposing the underlying representation.

// Iterator pattern
CLASS Fibonacci IMPLEMENTS Iterator
    FIELDS: a, b

    FUNCTION NEXT()
        result ← self.a
        new_b ← self.a + self.b
        self.a ← self.b
        self.b ← new_b
        RETURN result

// Usage with iterator adapters (map, filter, take, collect)
first_10 ← Fibonacci(0, 1) |> TAKE(10) |> COLLECT()

Rust's iterator system is one of the best implementations of this pattern in any language. Zero-cost abstractions — iterators compile to the same code as hand-written loops.

State

Allow an object to change its behavior when its internal state changes. The object appears to change its class.

INTERFACE ConnectionState
    FUNCTION OPEN() -> ConnectionState
    FUNCTION CLOSE() -> ConnectionState
    FUNCTION SEND(data) -> ConnectionState

CLASS Closed IMPLEMENTS ConnectionState
    FUNCTION OPEN()
        PRINT "Opening connection..."
        RETURN new Open
    FUNCTION CLOSE()       RETURN self          // already closed
    FUNCTION SEND(data)
        PRINT "Error: connection closed"
        RETURN self

CLASS Open IMPLEMENTS ConnectionState
    FUNCTION OPEN()        RETURN self          // already open
    FUNCTION CLOSE()
        PRINT "Closing connection..."
        RETURN new Closed
    FUNCTION SEND(data)
        PRINT "Sending: ", data
        RETURN self

Rust idiom: Often implemented with enums (algebraic state machines) rather than trait objects:

TYPE Connection = Closed | Open(stream) | Error(message)

When to use: Objects with distinct states and different behavior per state. State machines (protocols, game states, UI flows).

Template Method

Define the skeleton of an algorithm in a base, letting subclasses override specific steps.

INTERFACE DataMiner
    FUNCTION EXTRACT(source) -> bytes        // to be overridden
    FUNCTION PARSE(data) -> list of Record   // to be overridden
    FUNCTION ANALYZE(records) -> Report      // to be overridden

    // Template method — defines the algorithm structure
    FUNCTION MINE(source) -> Report
        raw ← self.EXTRACT(source)
        records ← self.PARSE(raw)
        RETURN self.ANALYZE(records)

CLASS CsvMiner IMPLEMENTS DataMiner
    FUNCTION EXTRACT(source)   // read CSV file
    FUNCTION PARSE(data)       // parse CSV
    FUNCTION ANALYZE(records)  // default analysis

When to use: Algorithm with fixed structure but customizable steps. Framework hooks (setup/teardown in test frameworks).

Visitor

Separate an algorithm from the object structure it operates on. Add new operations without modifying the objects.

INTERFACE Visitor
    PROCEDURE VISIT_FILE(file)
    PROCEDURE VISIT_DIRECTORY(dir)

INTERFACE Visitable
    PROCEDURE ACCEPT(visitor)

CLASS File IMPLEMENTS Visitable
    PROCEDURE ACCEPT(visitor)  visitor.VISIT_FILE(self)

CLASS Directory IMPLEMENTS Visitable
    PROCEDURE ACCEPT(visitor)
        visitor.VISIT_DIRECTORY(self)
        FOR EACH child IN self.children
            child.ACCEPT(visitor)

// New operation: just implement a new Visitor
CLASS SizeCalculator IMPLEMENTS Visitor
    FIELDS: total ← 0
    PROCEDURE VISIT_FILE(file)       self.total ← self.total + file.size
    PROCEDURE VISIT_DIRECTORY(dir)   // directories have no inherent size

When to use: Multiple operations on a complex object structure. Adding operations without modifying the objects. Compilers (AST traversal for type checking, code generation, optimization — each a different visitor).

Other Behavioral Patterns

Mediator

Define an object that encapsulates how a set of objects interact. Promotes loose coupling by preventing objects from referring to each other directly.

Example: Chat room mediates communication between users. Air traffic control mediates between planes.

Memento

Capture an object's internal state so it can be restored later. Without violating encapsulation.

Example: Undo mechanism (save state before change, restore on undo). Game save/load.

Chain of Responsibility

Pass a request along a chain of handlers. Each handler either processes the request or passes it to the next.

// Middleware chain in a web framework
FUNCTION LOGGING(req, next)
    PRINT "Request: ", req.method, " ", req.path
    resp ← next.HANDLE(req)
    PRINT "Response: ", resp.status
    RETURN resp

Example: Middleware (web frameworks), event bubbling (UI), exception handling.

Interpreter

Define a grammar and an interpreter for a language. Each grammar rule = a class.

When to use: Simple languages (regex, math expressions, config files, query languages). Often replaced by parser libraries in practice.

Applications in CS

  • Web frameworks: Observer (event handlers), chain of responsibility (middleware), strategy (routing), template method (request lifecycle).
  • Game development: State (game states), command (input handling, undo), observer (event system), strategy (AI behaviors).
  • Compilers: Visitor (AST traversal), interpreter (evaluation), iterator (token streams).
  • Editors: Command (undo/redo), memento (snapshots), observer (UI updates).
  • Distributed systems: Observer (pub/sub), mediator (message broker), chain of responsibility (request routing).