BEHAVIORAL PATTERNS / OBJECT DESIGN BRIEF

Chain of Responsibility pattern

Chain of Responsibility passes a request along a chain of handlers; each handler either processes it, passes it on, or both.

IntermediatePhase 06 / Topic 6 of 10ResponsibilitiesCollaborationsExtensibility
01

Overview

Chain of Responsibility passes a request along a chain of handlers; each handler either processes it, passes it on, or both. The sender does not know which handler will act. Handlers can be added, removed, or reordered without changing the sender.

It is the pattern behind middleware pipelines (auth, rate limit, logging, validation), approval workflows (manager, director, VP approve by amount), support escalation, and ATM cash dispensing by denomination. Each handler has one responsibility, and the chain composes them.

Customer support escalation

Your question goes to a chatbot first, then a support agent, then a specialist, then a manager. Each one either solves it or passes it up. You do not need to know who will ultimately answer.

02

When to use it

  • Several objects may handle a request, decided at runtime.
  • A pipeline of checks or processing steps.
  • Escalation by level or amount.
03

Where it shows up in interviews

Approval chains

Recognize it when: approval depends on amount or level.

  • Design an expense approval system
  • Design a loan approval workflow
Request pipelines

Recognize it when: ordered checks before handling.

  • Design an API middleware pipeline
  • Design a logging framework with levels
Denomination breakdown

Recognize it when: split an amount across handlers.

  • Design an ATM cash dispenser
  • Design a vending machine change maker
04

Where it is used in real software

Express, Koa, ASP.NET middleware

Each middleware calls next() to pass control down the chain.

Java Servlet filters and Spring Security

Security filter chains handle authentication and authorization in order.

Logging frameworks

Loggers pass events up to parent loggers and appenders.

05

Key terms

Handler
Processes or forwards a request.
Successor / next
The next handler in the chain.
Short-circuit
Stop the chain (reject or fully handle).
Pipeline
Every handler processes and passes on.
06

How it works, step by step

  1. 1
    Define the handler interface

    handle(request) with a next reference.

  2. 2
    Implement one concern per handler

    Auth, rate limit, validation.

  3. 3
    Decide: handle, pass, or stop

    Each handler chooses.

  4. 4
    Assemble the chain

    Order matters; build in one place.

  5. 5
    Handle the end of the chain

    Default handler or error if unhandled.

ATM dispensing $380
Step 1 / 4
$100 handler
$50 handler
$20 handler
$10 handler

STEP 1380 / 100 = 3 notes. Remaining 80 passed on.

07

Expense approval chain

TeamLead approves <= 1,000; Manager <= 10,000; Director <= 50,000; else CFO

Step 1 / 4
ExpenseTeamLeadManagerDirectorApproved by
$300Approves--TeamLead
$4,500PassesApproves-Manager
$22,000PassesPassesApprovesDirector
$80,000PassesPassesPassesCFO

NOWExpense: $300 | TeamLead: Approves | Manager: - | Director: - | Approved by: TeamLead

Changing limits or adding a level edits the chain assembly, not the request code.

08

Implementation

public abstract class CashHandler {    private CashHandler next;    private final int denomination;    private int available;     protected CashHandler(int denomination, int available) {        this.denomination = denomination;        this.available = available;    }     public CashHandler then(CashHandler next) { this.next = next; return next; }     public void dispense(int amount, Map<Integer, Integer> out) {        int notes = Math.min(amount / denomination, available);        if (notes > 0) {            out.put(denomination, notes);            available -= notes;            amount -= notes * denomination;        }        if (amount == 0) return;        if (next == null) throw new IllegalStateException("Cannot dispense remaining " + amount);        next.dispense(amount, out);    }} final class Hundreds extends CashHandler { Hundreds(int n) { super(100, n); } }final class Fifties extends CashHandler { Fifties(int n) { super(50, n); } }final class Twenties extends CashHandler { Twenties(int n) { super(20, n); } }final class Tens extends CashHandler { Tens(int n) { super(10, n); } } CashHandler chain = new Hundreds(10);chain.then(new Fifties(10)).then(new Twenties(20)).then(new Tens(20));Map<Integer, Integer> notes = new LinkedHashMap<>();chain.dispense(380, notes); // {100=3, 50=1, 20=1, 10=1}
09

Complexity and performance

Request costO(handlers)

Worst case passes through all.

Adding a handlerO(1)

Insert into chain.

10

Trade-offs

Flexibility vs guarantees

A request may fall off the end unhandled; add a default handler.

Debugging

Following a request through many handlers needs logging or tracing.

11

Variants and related techniques

Pipeline (all handle)

Middleware where every handler processes and passes on.

First match wins

Stop at the first handler that can handle.

12

Common mistakes

  • Order-dependent bugs.

    Fix: Build the chain in one place and test the order (auth before rate limit, etc.).

  • Handlers doing multiple jobs.

    Fix: One concern per handler.

13

Interview questions

How would you design an ATM's cash dispenser?

Chain of Responsibility: a handler per denomination from largest to smallest; each dispenses as many notes as possible within its stock and passes the remainder on; if the last handler cannot finish, reject and roll back.

Chain of Responsibility vs Decorator?

Both link objects. Decorators always delegate and add behavior around the call; chain handlers may stop the request and decide whether to pass it on.

14

Practice problems

ProblemDifficultyWhat it trains
Expense approval chainEasyEscalation.
ATM dispenser with limited notesMediumRollback on failure.