Chapter 04
Behavioural patterns: requests and events
Patterns for passing requests, events and state between objects without tying them tightly together.
Chain of Responsibility
Also called middleware, handler chain, pipeline
- What
- Intent: pass a request along a chain of handlers until one handles it or the chain ends.
- Why
- When: several independent checks or steps apply to a request, such as authentication, rate limiting and validation middleware.
type Handler = (req: Req, next: () => void) => void;
const chain: Handler[] = [authenticate, rateLimit, validate];
function run(req: Req, i = 0) {
if (i < chain.length) chain[i](req, () => run(req, i + 1));
}Command
Also called action object, undo
- What
- Intent: turn a request into an object with everything needed to perform it, and often to undo it.
- Why
- When: you need undo and redo, queues, scheduled jobs, macros or a log of actions to replay.
interface Command { run(): void; undo(): void; }
class AddText implements Command {
constructor(private doc: Doc, private text: string) {}
run() { this.doc.append(this.text); }
undo() { this.doc.trimEnd(this.text.length); }
}
history.push(cmd); cmd.run();Iterator
Also called cursor, generator
- What
- Intent: walk through a collection one item at a time without exposing how it is stored.
- Why
- When: callers should loop over a tree, a paginated API or a custom structure with a plain
forloop. Most languages build it in.
class Paged implements Iterable<Row> {
*[Symbol.iterator]() {
for (let p = 1; ; p++) {
const rows = fetchPage(p);
if (!rows.length) return;
yield* rows;
}
}
}
for (const row of new Paged()) show(row);Mediator
Also called hub, controller
- What
- Intent: objects talk through one mediator instead of referring to each other directly.
- Why
- When: many components interact in tangled ways, such as form fields that enable and fill each other, or chat room users.
- Example
- A booking form's date picker, slot list and price box each report changes to a form controller, which decides what the others should show.
Watch out
Letting the mediator absorb all logic until it becomes a god object.
Memento
Also called snapshot
- What
- Intent: capture an object's internal state as a snapshot so it can be restored later, without exposing its internals.
- Why
- When: you need undo, drafts, checkpoints or rollback in editors, games and forms.
class Editor {
private text = '';
save() { return { text: this.text } as const; }
restore(m: { text: string }) { this.text = m.text; }
}Observer
Also called publish-subscribe, pub/sub, event listener, listener
- What
- Intent: when one object changes, all objects subscribed to it are notified automatically.
- Why
- When: many parts react to one event, such as UI updates, webhooks and domain events.
addEventListenerand RxJS are built on it.
class Emitter<T> {
private subs = new Set<(v: T) => void>();
on(fn: (v: T) => void) {
this.subs.add(fn);
return () => this.subs.delete(fn); // unsubscribe
}
emit(v: T) { this.subs.forEach((fn) => fn(v)); }
}Watch out
Forgetting to unsubscribe, which leaks memory and fires handlers on screens that are gone.