โ† 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"