The Chain of Responsibility pattern is a behavioural design pattern that allows passing a request along a chain of potential handlers until one of them handles it. Each handler in the chain either processes the request or passes it to the next handler.

This pattern decouples the sender of the request from its receiver, giving multiple objects the chance to handle the request dynamically.

Applicability

  • When more than one object may handle a request, and the handler isn’t known in advance.
  • When you want to issue a request to one of several objects without tightly coupling the sender to the receiver.
  • When processing steps should be dynamically composed or reordered.

Pros and Cons

Advantages

  • Decouples sender and receiver
    • The sender doesn’t need to know which object will handle the request.
  • Promotes flexibility
    • Handlers can be changed or added dynamically without modifying the request sender.
  • Follows Single Responsibility Principle
    • Each handler deals with a specific kind of request, keeping responsibilities clear and focused.

Drawbacks

  • No guarantee that a request will be handled
    • If no handler handles the request, it might be dropped silently unless handled explicitly.
  • Debugging is harder
    • It can be tricky to trace the request flow, especially when chains are long or dynamically built.
  • Reduced performance
    • Especially if the chain is long and each handler does non-trivial checks.

Approach

  1. Define a Handler interface that declares a method for handling requests and for setting the next handler.
  2. Implement ConcreteHandlers that either handle the request or pass it to the next handler in the chain.
  3. The Client builds the chain and submits requests to the first handler.

Components

  • Handler (interface or abstract class)
    • Declares a method for handling requests and optionally storing a reference to the next handler.
  • ConcreteHandler
    • Handles the request it is responsible for, or passes it along the chain.
classDiagram
    class Handler {
        <<abstract>>
        -successor: Handler
        +__init__(successor: Handler)
        +handle_request(request)
        +can_handle(request)* abstract
    }
    
    class ConcreteHandlerA {
        +can_handle(request)
        +handle_request(request)
    }
    
    class ConcreteHandlerB {
        +can_handle(request)
        +handle_request(request)
    }
    
    Handler <|-- ConcreteHandlerA : implements
    Handler <|-- ConcreteHandlerB : implements
    Handler --> Handler : successor

Example

Let’s consider a basic Support Request example, where a request is handled by Level 1, Level 2, or Manager support.

Handler

interface SupportHandler {
    void setNext(SupportHandler next);
    void handleRequest(String level);
}

Concrete handler

class Level1Support implements SupportHandler {
    private SupportHandler next;
 
    @Override
    public void setNext(SupportHandler next) {
        this.next = next;
    }
 
    @Override
    public void handleRequest(String level) {
        if (level.equals("level1")) {
            System.out.println("Level 1 support handled the request.");
        } else if (next != null) {
            next.handleRequest(level);
        } else {
            System.out.println("No handler available for: " + level);
        }
    }
}
 
class Level2Support implements SupportHandler {
    private SupportHandler next;
 
    @Override
    public void setNext(SupportHandler next) {
        this.next = next;
    }
 
    @Override
    public void handleRequest(String level) {
        if (level.equals("level2")) {
            System.out.println("Level 2 support handled the request.");
        } else if (next != null) {
            next.handleRequest(level);
        } else {
            System.out.println("No handler available for: " + level);
        }
    }
}
 
class ManagerSupport implements SupportHandler {
    private SupportHandler next;
 
    @Override
    public void setNext(SupportHandler next) {
        this.next = next;
    }
 
    @Override
    public void handleRequest(String level) {
        if (level.equals("manager")) {
            System.out.println("Manager handled the request.");
        } else if (next != null) {
            next.handleRequest(level);
        } else {
            System.out.println("No handler available for: " + level);
        }
    }
}

Client

public class ChainOfResponsibilityExample {
    public static void main(String[] args) {
        // Create handlers
        SupportHandler level1 = new Level1Support();
        SupportHandler level2 = new Level2Support();
        SupportHandler manager = new ManagerSupport();
 
        // Set up chain
        level1.setNext(level2);
        level2.setNext(manager);
 
        // Client sends requests
        level1.handleRequest("level1");
        level1.handleRequest("level2");
        level1.handleRequest("manager");
        level1.handleRequest("unknown");
    }
}
 
Level 1 support handled the request.
Level 2 support handled the request.
Manager handled the request.
No handler available for: unknown

Back to parent page: Behavioural Patterns

Design_Pattern Behavioural_Design_Patterns Chain_Of_Responsibility SOFT2201