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).