Chain of Responsibility Pattern

25 Jul 2026 4 min read
0
The Chain of Responsibility Pattern is a behavioral design pattern that allows a request to be passed through a chain of handlers until one of them processes it.

Instead of the client invoking a specific handler directly, it sends the request to the first handler in the chain. Each handler decides whether it can process the request or forward it to the next handler.

This pattern reduces the coupling between the client and the request handlers, making it easy to add, remove, or reorder handlers without affecting the client.

The Chain of Responsibility Pattern is commonly used in authentication, authorization, logging, validation, request processing pipelines, and middleware frameworks.

Structure

The Chain of Responsibility Pattern consists of the following participants.

1. The Client creates a request and sends it to the first handler in the chain.
2. The abstract Handler defines the common interface for processing requests and maintaining the next handler.
3. Concrete Handlers process the request if they can; otherwise, they forward it to the next handler.

Java Implementation

Suppose an API request must pass through multiple validation steps before it is processed.

The request should be checked for authentication, authorization, and request validation. If any step fails, the request processing stops immediately.

First, create the request object.
public class Request {
    private final boolean authenticated;
    private final boolean authorized;
    private final boolean valid;

    public Request(boolean authenticated,
                   boolean authorized,
                   boolean valid) {
        this.authenticated = authenticated;
        this.authorized = authorized;
        this.valid = valid;
    }

    public boolean isAuthenticated() {
        return authenticated;
    }

    public boolean isAuthorized() {
        return authorized;
    }

    public boolean isValid() {
        return valid;
    }
}
Create the abstract handler.
public abstract class Handler {
    private Handler nextHandler;

    public Handler setNext(Handler nextHandler) {
        this.nextHandler = nextHandler;
        return nextHandler;
    }

    public void handle(Request request) {
        if (process(request) && nextHandler != null) {
            nextHandler.handle(request);
        }
    }

    protected abstract boolean process(Request request);
}
Create the authentication handler.
public class AuthenticationHandler extends Handler {
    @Override
    protected boolean process(Request request) {

        if (!request.isAuthenticated()) {
            System.out.println("Authentication failed.");
            return false;
        }

        System.out.println("Authentication successful.");
        return true;
    }
}
Create the authorization handler.
public class AuthorizationHandler extends Handler {
    @Override
    protected boolean process(Request request) {

        if (!request.isAuthorized()) {
            System.out.println("Authorization failed.");
            return false;
        }

        System.out.println("Authorization successful.");
        return true;
    }
}
Create the validation handler.
public class ValidationHandler extends Handler {
    @Override
    protected boolean process(Request request) {

        if (!request.isValid()) {
            System.out.println("Validation failed.");
            return false;
        }

        System.out.println("Validation successful.");
        return true;
    }
}
The client builds the chain and sends the request.
public class Main {
    public static void main(String[] args) {

        Handler authentication = new AuthenticationHandler();
        Handler authorization = new AuthorizationHandler();
        Handler validation = new ValidationHandler();

        authentication.setNext(authorization).setNext(validation);

        Request request = new Request(true, true, true);
        authentication.handle(request);
    }
}
Output:
Authentication successful.
Authorization successful.
Validation successful.

How It Works?

The client sends the request only to the first handler in the chain, which in this example is the AuthenticationHandler.

If authentication succeeds, the request is forwarded to the AuthorizationHandler. If authorization also succeeds, the request is passed to the ValidationHandler.

Each handler performs only its own responsibility and remains unaware of the implementation details of the other handlers.

If any handler cannot process the request, it stops the chain by returning false, preventing the remaining handlers from executing.

This approach makes it easy to insert additional handlers, such as rate limiting, logging, auditing, or request transformation, without modifying the existing handlers or the client code.

Advantages

1. It reduces coupling between the client and request handlers.
2. New handlers can be added, removed, or reordered without changing existing code.
3. Each handler has a single responsibility, making the code easier to maintain.
4. It enables flexible request processing pipelines.

Disadvantages

1. Requests may pass through multiple handlers before being processed, introducing additional overhead.
2. If the chain is configured incorrectly, requests may never reach the appropriate handler.
3. Debugging can become more difficult when the chain contains many handlers.

JDK Examples

The Chain of Responsibility Pattern is widely used throughout the Java ecosystem.

1. The java.util.logging.Logger forwards log records to its parent logger when it cannot handle them directly.

2. The javax.servlet.FilterChain processes HTTP requests by passing them through a sequence of servlet filters before reaching the target servlet.

Summary

The Chain of Responsibility Pattern passes a request through a sequence of handlers until it is processed or rejected.

By separating each processing step into an independent handler, the pattern promotes flexibility, extensibility, and maintainability while reducing coupling between the client and the request processing logic.
Nagesh Chauhan

Nagesh Chauhan

Principal Software Engineer • Java • Python • Distributed Systems • AI/ML

Principal Software Engineer with 14+ years of experience designing and delivering large-scale distributed systems, cloud-native applications, and AI-powered platforms.

Passionate about solving complex engineering problems using strong data structures and algorithms, along with expertise in Java, Spring Boot, Python, System Design, Microservices, Cloud, Kafka, Elasticsearch, and Generative AI.

Share this Article

💬 Comments

Join the Discussion