Behavioral PatternINTERMEDIATE7 min deep dive

Chain of Responsibility Pattern

Passes requests along a chain of handlers. Upon receiving a request, each handler decides either to process it or to pass it to the next handler in the chain.

Real-World Mental Model / Metaphor:

A customer support phone tree. The first automated agent handles FAQs. If unhandled, it transfers to Tier-1 support. If unresolved, it escalates to Tier-2 engineering.

Interactive Runtime Simulator

Test and tweak inputs dynamically

Interactive Chain of Responsibility SimulatorLIVE PLAYGROUND

Interact with the controls below to see how this design pattern executes in memory at runtime.

Category: Behavioral
Configure Chain Handlers (Toggle Failures):
CHAIN PIPELINE STATUS
Step 1: AuthHandler.check() -> PASSED
Step 2: RateLimitHandler.check() -> PASSED
Step 3: PayloadGuard.check() -> PASSED
The Problem It Solves

Hardcoding a sequence of checks (authentication, rate limiting, logging, caching) into a monolithic function that is difficult to reorder or extend.

The Architectural Solution

Chain handlers together. Each handler has a reference to `next`. A handler executes its logic and calls `next.handle(request)`.

Pattern Structure & Participants

ParticipantRole & ResponsibilityType
Handler (Interface / Abstract)Declares handle() and setNext()interface
AuthHandler, RateLimitHandlerConcrete handlers performing checksclass
ClientAssembles chain and passes requestclient
Collaboration Rules:
  • Chain: AuthHandler -> RateLimitHandler -> CacheHandler -> Controller
  • If any handler fails, it aborts the chain early

Production Code Implementation

Chain of Responsibility.java
public abstract class Middleware {
    private Middleware next;

    public Middleware linkWith(Middleware next) {
        this.next = next;
        return next;
    }

    public abstract boolean check(String user, String role);

    protected boolean checkNext(String user, String role) {
        if (next == null) return true;
        return next.check(user, role);
    }
}

// Concrete Handlers
public class RateLimitMiddleware extends Middleware {
    public boolean check(String user, String role) {
        System.out.println("Checking rate limit for: " + user);
        // If rate limit breached, abort chain early!
        return checkNext(user, role);
    }
}

public class RoleAuthMiddleware extends Middleware {
    public boolean check(String user, String role) {
        System.out.println("Verifying admin privileges for role: " + role);
        if (!"ADMIN".equals(role)) {
            System.out.println("403 Forbidden: Insufficient permissions");
            return false;
        }
        return checkNext(user, role);
    }
}

// Chain Assembly:
// Middleware chain = new RateLimitMiddleware();
// chain.linkWith(new RoleAuthMiddleware());
// chain.check("john_doe", "USER");
When to Use:
  • •When your program is expected to process different kinds of requests in various ways, but the exact types of requests and their sequences are unknown beforehand.
  • •When it is essential to execute several handlers in a specific order.
  • •When the set of handlers and their order are supposed to change at runtime.
When to Avoid (Anti-Pattern):
  • •When requests must always be handled by one specific class and no chaining logic exists.

Staff Engineering Interview Insights & Gotchas

  • Every modern web framework's middleware pipeline (Express, Spring, ASP.NET) is an implementation of Chain of Responsibility.
  • Show how returning `false` or throwing an exception halts the chain immediately (fail-fast).