Learn the Chain of Responsibility Design Pattern
This is the 16th post in a series on design patterns.
Chain of Responsibility is a behavioral design pattern that lets you pass requests along a chain of handlers. Upon receiving a request, each handler decides either to process the request or to pass it to the next handler in the chain.
Imagine you’re working on an online ordering system. You want to restrict access to the system to authorized users only. Therefore, you added checks before the order enters the ordering system. You added several more checks as the days went by, including the validation of request data, repeated failed requests coming from the same IP address, and checking against cache responses before a request can be entered into the online ordering system.
As you add more checks to the code, it becomes bloated.
Chain of Responsibility Design Pattern
The Chain of Responsibility involves transforming certain behaviors into standalone objects called handlers. We should have extracted each check into its own class with a single method to perform it. This method receives the request along with its data as an argument.
This pattern suggests linking these handlers together in a chain. In each linked handler, there is a field for storing a reference to the next handler in the chain. Handlers pass requests along the chain in addition to processing them. Requests are routed along the chain until all handlers have had a chance to process them.
Additionally, a handler can decide not to pass the request further down the chain, effectively stopping further processing.
UML Class Diagram
Not familiar with UML Class Diagram? I have written a detailed post on the UML Class diagram.
Implementation steps
- Declare the handler interface and describe the signature of a method to handle requests. Determine how the client will pass the request data to the method. Converting the request into an object and passing it as an argument to the handling method is the most flexible way to handle it.
- It might be worth creating an abstract handler class, derived from the handler interface, to eliminate duplicate boilerplate code in concrete handlers. The class should have a field for storing a reference to the next handler in the chain.
- Create concrete handler subclasses and implement their methods one by one. When a request is received, each handler should make two decisions:
* Whether to process the request.
* Whether to forward the request along. - The client can either assemble chains on its own or receive pre-built chains from other objects. Any handler in the chain can be triggered by the client, not just the first. It will be passed along the chain until some handler refuses to pass it further or until it reaches the end.
Source Code Implementation
Handlers declare a common interface for all concrete handlers. It usually contains just a single method for handling requests, but sometimes it may also contain a method for setting the next handler.
Base Handler is an optional class where you can put boilerplate code common to all handler classes. Typically, this class defines a field to store a reference to the next handler.
Concrete Handlers contain the actual code for handling requests. Upon receiving a request, each handler must decide whether to process it and, in addition, whether to pass it along the chain.
The NegativeNumberHandler class handles negative numbers.
The PositiveNumberHandler class handles positive numbers.
The ZeroNumberHandler class handles zero numbers.
The Client may compose chains just once or compose them dynamically, depending on the application’s logic.
// Output Processing zero number 0 Processing positive number 100 Processing negative number -10
When To Apply Chain of Responsibility Design Pattern
- Whenever your program is expected to handle different types of requests in various ways, but the types of requests and their sequences are not known in advance, use the Chain of Responsibility pattern.
- When several handlers must be executed in a specific order, use this pattern.
- Use this pattern when the order of handlers and their set is supposed to change at runtime.
Pros of Chain of Responsibility Design Pattern
- The order in which requests are handled can be controlled.
- You can decouple classes that invoke operations from classes that perform them.
- The application can be extended with new handlers without breaking the existing client code.