Last Updated: December 31, 2025
The Chain of Responsibility Design Pattern is a behavioral pattern that lets you pass requests along a chain of handlers, allowing each handler to decide whether to process the request or pass it to the next handler in the chain.
This pattern is useful when:
When dealing with conditional request handling, developers often resort to long chains of if-else or switch statements to determine how a request should be processed. For example, a logging system might write to the console, file, or remote server depending on configuration, or an HTTP request might need to go through validation, authentication, and rate-limiting steps.
But as the number of conditions grows, this approach becomes hard to scale, violates the Open/Closed Principle, and turns your logic into a tightly coupled, brittle monolith.
The Chain of Responsibility Pattern solves this by turning individual processing steps into standalone classes, each responsible for one specific concern. These handlers are linked together to form a chain, and the request flows through the chain until it is handled (or dropped).
Let’s walk through a real-world example to see how we can apply the Chain of Responsibility Pattern to build a clean, modular, and extensible pipeline for request processing.
Let us say you are building a backend server that processes incoming HTTP requests for a web application. Each request carries some information about the user, their role, how many requests they have made, and some payload data.
Before the request reaches the actual business logic, it must pass through several processing steps:
Only after all these checks pass should the request reach the actual business logic.
A typical first attempt might look like this: implement all logic inside a single class using a long chain of if-else statements.
This works fine for a simple case, but there are several problems hiding beneath the surface.
Every time you need to add a new check, say logging, caching, or metrics collection, you must modify the existing RequestProcessor class. The class is open for modification when it should be closed for changes and open for extension.
All validation and control logic is tightly coupled inside a single method. This violates the Single Responsibility Principle. The class is doing too many things: authentication, authorization, rate limiting, validation, and business logic coordination.
What if another service needs the same authentication logic? You would have to copy the code or create awkward shared methods. Neither option is clean.
What if you want to skip authorization for public APIs? Or make validation optional in development mode? You would need more if statements, and the code would become even more tangled.
We need a way to:
This is exactly what the Chain of Responsibility Pattern provides.
The Chain of Responsibility Pattern allows a request to be passed along a chain of handlers. Each handler in the chain can either:
This pattern decouples the sender of the request from the receivers, giving you the flexibility to compose chains dynamically, reuse logic, and avoid rigid conditional blocks.
The pattern consists of three main components:
handle(request) for processing the request.setNext(handler).AuthHandler, RateLimitHandler)Implement the Handler interface.
Each handler decides if it will:
setNext().Let’s refactor our monolithic RequestHandler into a clean, extensible chain of modular handlers using the Chain of Responsibility Pattern.
Every handler will implement this interface. Each handler should:
To avoid duplicating the setNext() and forwarding logic in every handler, we define an abstract base class with reusable functionality.
Now every concrete handler can focus solely on its logic and delegate to
forward(request)when needed.
Each handler implements one responsibility. They extend BaseHandler, implement handle(Request), and determine whether to continue the chain or short-circuit it.
This is the last handler in the chain — it assumes the request has passed all previous checks.
Now that our handlers are modular, we can connect them in any order depending on the requirements.