Chain of Responsibility Design Pattern in Java

1. Definition

The Chain of Responsibility Pattern creates a chain of receiver objects for a request. This pattern decouples the sender from the receiver by allowing more than one object to handle a request. The request is passed along the chain until an object handles it or it reaches the end of the chain.

2. Problem Statement

Imagine you have a series of processing objects, but you're unsure about which one should process a specific request. You want to simplify the sender by eliminating the need to decide which receiver should handle the request.

3. Solution

Chain of Responsibility pattern addresses this problem by chaining potential handlers, passing the request along the chain until an object handles it. Each processing object in the chain contains logic to decide whether it should process the request or pass it to the next object in line.

4. Real-World Use Cases

1. Event propagation in GUI libraries, where an event can be processed by either a component or its parent containers.

2. Middleware in web frameworks, where a request may be passed through multiple layers of processing.

3. Input validation and processing in a sequential manner.

5. Implementation Steps

1. Define an interface/handler that will act as the link in the chain. This will have a method to set the next link and another method to process the request.

2. Create concrete implementations for the handler, where each handler decides either to process the request or pass it to the next handler in the chain.

3. Clients create a chain by linking handlers and then pass the request through it.

6. Implementation

// Step 1: Define the handler interface
abstract class Handler {
    protected Handler successor;

    public void setSuccessor(Handler successor) {
        this.successor = successor;
    }

    public abstract void handleRequest(String request);
}

// Step 2: Implement concrete handlers
class FirstHandler extends Handler {
    @Override
    public void handleRequest(String request) {
        if ("Request1".equals(request)) {
            System.out.println("FirstHandler handled " + request);
        } else if (successor != null) {
            successor.handleRequest(request);
        }
    }
}

class SecondHandler extends Handler {
    @Override
    public void handleRequest(String request) {
        if ("Request2".equals(request)) {
            System.out.println("SecondHandler handled " + request);
        } else if (successor != null) {
            successor.handleRequest(request);
        }
    }
}

// Client code
public class ChainPatternDemo {
    public static void main(String[] args) {
        Handler first = new FirstHandler();
        Handler second = new SecondHandler();

        first.setSuccessor(second);

        first.handleRequest("Request1");
        first.handleRequest("Request2");
        first.handleRequest("Request3");
    }
}

Output:

FirstHandler handled Request1
SecondHandler handled Request2

Explanation

In the example above, FirstHandler and SecondHandler are two links in the chain of responsibility. A request is first sent to FirstHandler. If it can't handle it, it's passed on to its successor, which is SecondHandler in this case. If no handler can manage the request, it goes unhandled, as seen with "Request3".

7. When to use?

Use the Chain of Responsibility pattern when:

1. More than one object may handle a request, but only one should process it.

2. You want to decouple the sender from receiver objects.

3. The set of handlers can be dynamically specified or changed.


Comments