Design Patterns Chain of Responsibility

The Chain of Responsibility pattern passes a request along a line of handlers. Each handler either processes the request or forwards it to the next one. The sender never needs to know which handler ends up doing the work.

The Real-World Analogy

A company support desk sorts questions by difficulty. Front-line agents answer simple questions. Hard ones move to a senior agent, and rare ones reach a manager. The customer makes one call, and the right person responds.

Problem Without a Chain

class SupportDesk {
    handle(ticket) {
        if ticket.level == 1 → agent code
        else if ticket.level == 2 → senior code
        else if ticket.level == 3 → manager code
    }
}
All routing rules sit in one place. Adding a level means editing this class.

Solution With a Chain

Request ──▶ ┌─────────┐ ──▶ ┌─────────┐ ──▶ ┌─────────┐
            │ Agent   │     │ Senior  │     │ Manager │
            │ level 1?│     │ level 2?│     │ level 3?│
            └────┬────┘     └────┬────┘     └────┬────┘
                 │ handled       │ handled       │ handled
                 ▼               ▼               ▼
              Response        Response        Response

A handler that cannot solve the problem passes it onward. The chain ends when someone handles the request or the line runs out.

Structure

┌─────────────────────────┐
│ Handler                 │◀─────────┐ next
│ - next: Handler         │──────────┘
│ + setNext(Handler)      │
│ + handle(request)       │
└────────────▲────────────┘
     ┌───────┴────────┬──────────────┐
┌────┴─────┐   ┌──────┴─────┐   ┌────┴──────┐
│ AgentH   │   │ SeniorH    │   │ ManagerH  │
└──────────┘   └────────────┘   └───────────┘

Roles

  • Handler: declares the handle method and keeps a link to the next handler.
  • Concrete Handler: decides to act or forward.
  • Client: builds the chain and sends the first request.

Code Example

abstract class Handler {
    protected Handler next;

    Handler setNext(Handler next) {
        this.next = next;
        return next;
    }

    void handle(int level) {
        if (canHandle(level)) {
            process(level);
        } else if (next != null) {
            next.handle(level);          // pass along
        } else {
            print("No handler available");
        }
    }

    abstract boolean canHandle(int level);
    abstract void process(int level);
}

class Agent extends Handler {
    boolean canHandle(int level) { return level == 1; }
    void process(int level) { print("Agent solved it"); }
}

class Senior extends Handler {
    boolean canHandle(int level) { return level == 2; }
    void process(int level) { print("Senior solved it"); }
}

class Manager extends Handler {
    boolean canHandle(int level) { return level == 3; }
    void process(int level) { print("Manager solved it"); }
}

Handler agent = new Agent();
agent.setNext(new Senior()).setNext(new Manager());
agent.handle(2);    // Senior solved it

Two Flavors of Chains

A classic chain stops at the first handler that accepts the request. Pipeline variants let every handler act and forward. Web servers use the pipeline style, where each step adds logging, security checks, or compression before the request moves on.

Request ─▶ Logging ─▶ Authentication ─▶ Compression ─▶ Business Logic

Real Projects Using the Chain

ScenarioChain Links
Web request pipelineLogging, security, caching, controller
Form validationRequired check, format check, range check
Expense approvalTeam lead, department head, finance
Interface eventsButton, panel, window

Advantages and Disadvantages

AdvantagesDisadvantages
Sender and receiver stay separateRequests may reach no handler
Handlers reorder or join freelyLong chains slow debugging
Each handler owns one ruleOrder mistakes cause subtle errors

Step-by-Step Walkthrough

Follow agent.handle(2) in the code example. The Agent receives level 2 and checks canHandle(). False comes back, and the Agent forwards the call to its next link, the Senior. Accepting level 2, the Senior prints its message. The Manager never sees the request.

Changing the order or removing the Senior takes one line in the setup code. No handler needs an edit.

Designing Good Chains

  • Place the most common handlers near the front to save work.
  • Keep each handler focused on one rule.
  • End the chain with a catch-all handler that reports unhandled requests.
  • Document the intended order, since order changes behavior.

Common Mistakes

  • Forgetting to forward the request, which silently drops it.
  • Building circular chains that loop forever.
  • Letting two handlers accept the same request in a first-match chain.

Plain-Words Summary

Chains suit problems where the right handler is unknown at the time of the request. Letting the line of handlers sort things out keeps the sender simple and the rules flexible. Many developers meet this pattern first in web frameworks, where each step of a request pipeline checks one concern. Recognizing the shape makes framework documentation far easier to read.

Key Takeaways

  • A chain forwards a request until some handler takes responsibility.
  • Senders know only the first handler.
  • Handlers join, leave, or reorder without touching client code.
  • Always plan for the case where nobody handles the request.