โ Back to Design Patterns
โ๏ธ
Behavioral
Chain of Responsibility
Passes a request along a chain of handlers; each handler decides whether to handle it or forward it.
The Problem
Multiple objects may handle a request, but you don't know which one ahead of time. You want to avoid coupling the sender to a specific receiver.
How It Works
- โบEach handler has a reference to the next handler in the chain
- โบA handler either handles the request or passes it to the next
- โบThe sender does not need to know which handler will process the request
- โบHandlers can be added, removed, or reordered at runtime
- โบRequest may go unhandled if no handler processes it โ decide on a default
When to Use
โMore than one handler can process a request (middleware pipelines)
โWhich handler to use should be determined at runtime
โPermission checks, validation chains, event bubbling
โLogging pipelines, request interceptors
Real-World Examples
๐
iOS
UIResponder chain (touch events bubble up from subview to window), URLProtocol subclasses
๐ค
Android
OkHttp Interceptor chain, View.onTouchEvent propagation, Compose event handling
๐
Web
Express.js middleware (next()), Koa.js, DOM event bubbling, NestJS guards/interceptors
Code Examples
Swift
protocol RequestHandler: AnyObject {
var next: RequestHandler? { get set }
func handle(request: Int) -> String?
}
class LowHandler: RequestHandler {
var next: RequestHandler?
func handle(request: Int) -> String? {
guard request > 10 else { return "Low handled \(request)" }
return next?.handle(request: request)
}
}
class MediumHandler: RequestHandler {
var next: RequestHandler?
func handle(request: Int) -> String? {
guard request > 50 else { return "Medium handled \(request)" }
return next?.handle(request: request)
}
}
class HighHandler: RequestHandler {
var next: RequestHandler?
func handle(request: Int) -> String? { "High handled \(request)" }
}
let low = LowHandler(); let med = MediumHandler(); let high = HighHandler()
low.next = med; med.next = high
print(low.handle(request: 5)!) // "Low handled 5"
print(low.handle(request: 30)!) // "Medium handled 30"
print(low.handle(request: 99)!) // "High handled 99"