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.