Chain of Responsibility in 5 Simple Steps
The Chain of Responsibility pattern is a behavioural design pattern where a request is passed through a chain of handlers, and each handler decides either to process the request or pass it along the chain.
Scenario:
Consider a scenario where you have a series of processors, each responsible for a specific aspect of request handling. The order and responsibility of these processors may vary based on the requirements.
In a Spring Boot application, you might encounter scenarios where multiple components or services need to process a request, and the order or responsibility of each component can vary. The Chain of Responsibility pattern helps in handling these scenarios in a flexible and maintainable way.
Here’s a simple example in Java Spring Boot to illustrate the Chain of Responsibility pattern. Let’s consider a scenario where we have different processors to handle a request, and each processor can decide whether to process the request or pass it to the next processor:
- Define the Request class:
public class Request {
private String type;
public Request(String type) {
this.type = type;
}
public String getType() {
return type;
}
}
2. Create the Handler interface:
public interface RequestProcessor {
boolean process(Request request);
}
3. Implement concrete handlers:
@Component
public class FirstProcessor implements RequestProcessor {
@Override
public boolean process(Request request) {
if (request.getType().equals("AUTHENTICATION")) {
System.out.println("Handling Authentication Request");
// Logic for handling authentication
}else{
nextHandler.handleRequest(request);
}
}
}
@Component
public class SecondProcessor implements RequestProcessor {
@Override
public void process(Request request) {
if (request.getType().equals("AUTHORIZATION")) {
System.out.println("Handling Authorization Request");
// Logic for handling authorization
} else {
// Pass the request to the next handler in the chain
nextHandler.handleRequest(request);
}
}
}
If a processor handles the request, the chain is broken, and further processors are not called.
4. Create a Chain of Responsibility to handle all requests:
@Service
public class RequestService {
private final List<RequestProcessor> processors;
@Autowired
public RequestService(List<RequestProcessor> processors) {
this.processors = processors;
}
public void processRequest(Request request) {
for (RequestProcessor processor : processors) {
if (processor.process(request)) {
// Request handled, break the chain
break;
}
}
}
}
The RequestService
is responsible for orchestrating the processing of a request through the chain of processors.
Usage:
Now, let’s use this chain of processors in a Spring Boot application:
5. Usage in a Spring Boot Application:
@SpringBootApplication
public class ChainOfResponsibilityExampleApplication implements CommandLineRunner {
@Autowired
private RequestService requestService;
public static void main(String[] args) {
SpringApplication.run(ChainOfResponsibilityExampleApplication.class, args);
}
@Override
public void run(String... args) {
// Create a sample request
Request request = new Request("Sample Request Data");
// Process the request through the chain of processors
requestService.processRequest(request);
}
}
Why Chain of Responsibility:
- Flexibility and Extensibility: The Chain of Responsibility pattern allows you to add or remove processors without modifying existing code. This promotes flexibility and extensibility in handling different types of requests.
- Loose Coupling: The pattern promotes loose coupling between the sender of the request (
RequestService
) and the handlers (RequestProcessor
implementations). Each processor is only aware of the next processor in the chain, minimizing dependencies. - Dynamic Processing Order: The order of processors and their responsibilities can be easily changed at runtime without affecting the core logic of the application. This is useful when requirements dictate a dynamic order of processing.
- Single Responsibility Principle: Each processor focuses on a specific aspect of handling the request, adhering to the Single Responsibility Principle. This makes the code modular and easier to maintain.