Chain of Responsibility Pattern
Chain of Responsibility Pattern
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.
Intent
The Chain of Responsibility decouples request senders from receivers by threading the request through a sequence of handler objects. Each handler in the chain either handles the request or passes it to the next handler. The sender never knows which object will ultimately respond — or whether any will. This allows the handler set to be assembled and altered at runtime without touching the sender.
When NOT to Use
- Every request must be guaranteed to be handled — a chain can silently swallow requests that reach the end without a handler
- The chain length is unbounded and performance is critical — requests traverse every preceding handler before reaching the right one
- Simple
if/elseorswitchdispatch is clearer — a chain adds indirection without benefit when the handler selection logic is trivial
When to Use
- The request sender should not need to know which handler will process it
- More than one handler may be a candidate and the appropriate handler should be determined at runtime
- The set of handlers changes at runtime (middleware pipelines, approval workflows, event filters)
How It Works
Participants:
- Handler — abstract base with an optional successor reference and a
handle(request)method - ConcreteHandler — either processes the request (when its condition is met) or calls
this.successor.handle(request)to forward it - Client — builds the chain by wiring handlers, then submits requests to the first handler
The chain terminates when a ConcreteHandler handles the request or the end of the chain is reached. Handlers are unaware of the full chain; each only knows its immediate successor.
Sequence Diagram
sequenceDiagram
participant Client
participant HandlerA
participant HandlerB
participant HandlerC
Client->>HandlerA: handle(request)
Note right of HandlerA: Can I handle this?
HandlerA->>HandlerA: check condition
HandlerA-->>HandlerB: pass to next
Note right of HandlerB: Can I handle this?
HandlerB->>HandlerB: check condition
HandlerB-->>HandlerC: pass to next
Note right of HandlerC: Yes -- handle it
HandlerC->>HandlerC: process request
HandlerC-->>Client: response
Note over HandlerA,HandlerC: Each handler either processes the request or forwards it along the chain
TypeScript Example
// Chain of Responsibility — TypeScript
// Source: GoF Design Patterns, p.223
abstract class Handler {
protected successor: Handler | null = null;
setNext(h: Handler): Handler { this.successor = h; return h; }
abstract handle(request: number): void;
}
class LowPriorityHandler extends Handler {
handle(request: number) {
if (request < 10) { console.log(`Low handled: ${request}`); }
else { this.successor?.handle(request); }
}
}
class HighPriorityHandler extends Handler {
handle(request: number) {
if (request >= 10) { console.log(`High handled: ${request}`); }
else { this.successor?.handle(request); }
}
}
// Chain: low → high
const low = new LowPriorityHandler();
low.setNext(new HighPriorityHandler());
low.handle(5); // Low handled: 5
low.handle(15); // High handled: 15Java Example
// Chain of Responsibility — Java
// Source: GoF Design Patterns, p.223
abstract class Handler {
protected Handler successor;
public void setSuccessor(Handler s) { this.successor = s; }
public abstract void handleRequest(int request);
}
class LowPriorityHandler extends Handler {
public void handleRequest(int request) {
if (request < 10) { System.out.println("Low handled: " + request); }
else if (successor != null) { successor.handleRequest(request); }
}
}
class HighPriorityHandler extends Handler {
public void handleRequest(int request) {
if (request >= 10) { System.out.println("High handled: " + request); }
else if (successor != null) { successor.handleRequest(request); }
}
}
// Client: low.setSuccessor(new HighPriorityHandler()); low.handleRequest(5);Related Concepts
| Pattern | Relationship |
|---|---|
| Decorator-Pattern | Decorator passes through all wrappers unconditionally; CoR stops at the first handler that claims the request |
| Command-Pattern | Command encapsulates a request as an object; CoR dispatches that request through a handler chain |
| Composite-Pattern | Composite forms a tree; CoR forms a linear chain — both propagate calls through a structure |
Sources
- Gamma, Helm, Johnson, Vlissides — Design Patterns: Elements of Reusable Object-Oriented Software, Addison-Wesley, 1994, p.223
Backlinks
Notes that link here: GoF-Patterns-MOC