design patterns · medium
Chain of Responsibility Pattern
Chain of Responsibility is a behavioral GoF pattern that avoids coupling a request's sender to its receiver by giving several objects a chance to handle it: handlers are linked into a chain and the request is passed along until one handles it. Each Handler holds a pointer to the next and implements one rule — if it can handle the request it does so (often stopping the chain), otherwise it forwards to the next handler; a handler may also process the request and still forward (e.g. a logger). The chain is assembled at runtime (auth → rate-limit → route), so links can be reordered, inserted, or removed without changing the handlers or the sender, which only drops the request at the head and never learns who ultimately handles it. This replaces a central if/else or switch that hardcodes who-handles-what, and it honors single-responsibility (one handler per concern) and open/closed (add handlers without editing existing ones). Its weaknesses: there is no guarantee the request is handled — it can fall off the end unprocessed unless a default/terminal handler is added — the dynamic flow is harder to debug, and long chains add per-request overhead. It is the pattern behind HTTP middleware pipelines, servlet filters, GUI event bubbling, log-level filtering, and approval workflows with escalating authority.
Chain of Responsibility (behavioral): link handlers and pass a request along the chain until one handles it, decoupling the sender from the receiver. Each handler either consumes the request (and stops) or forwards to next_; the chain is assembled at runtime so handlers can be reordered/added without touching the sender. It replaces a central if/else, honors SRP/Open-Closed, but a request can fall off the end unhandled (add a default). Textbook uses: middleware stacks, approval/escalation, event bubbling.
The code
// Chain of Responsibility — pass a request along a chain of handlers// until one handles it. Sender is decoupled from the receiver.struct Handler { Handler* next_ = nullptr; Handler* setNext(Handler* n) { next_ = n; return n; } // build chain virtual void handle(Request& r) { if (next_) next_->handle(r); // default: pass it on } virtual ~Handler() = default;};
struct Auth : Handler { void handle(Request& r) override { if (!r.token) { r.reject("401"); return; } // handled: STOP Handler::handle(r); // else pass on }};struct RateLimit : Handler { void handle(Request& r) override { if (over(r.ip)) { r.reject("429"); return; } Handler::handle(r); }};struct Route : Handler { void handle(Request& r) override { r.dispatch(); }};
// Build once, send blindly: auth -> rate -> routeauth.setNext(&rate)->setNext(&route);auth.handle(req); // sender doesn't know who finally handles itWhat this lesson walks through
- 01Intent — decouple sender from receiver
- 02Structure — handle-or-forward links
- 03The request walks until someone handles it
- 04Gotcha — the unhandled request
Chain of Responsibility lets a request pass along a line of handlers until one of them deals with it. The sender doesn't know WHICH handler will respond — it just drops the request at the head of the chain.
See it animated — step by step, at your own pace
Unlock the full interactive walkthrough of Chain of Responsibility Pattern and 100+ animated C++ interview lessons.