08 Jan 2025

Chain Of Responsibility Pattern

Overview

  • Pattern Type: Behavioral

  • Purpose: Allows passing requests along a chain of potential handlers until one of them handles the request. This pattern provides a way to process varying requests in a decentralized manner.

Key Components

  1. Handler (Interface or Abstract Class): Provides a blueprint for handling requests and optionally implementing the successor link. Each handler decides whether to handle the request or pass it along the chain.

  2. Concrete Handlers: Implement the handler interface and handle the requests they are responsible for. If they can't handle a request, they pass it on to the next handler in the chain.

  3. Client: Initiates the request to the first handler in the chain.

Real-World Example

  • Context: A customer support system where a request (like a refund request) can be handled by multiple levels of personnel (e.g., a support agent, a supervisor, and a manager), depending on the complexity or amount of the refund.

Code Example (in Java)

// Handler
public abstract class Handler {
    protected Handler successor;

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

    public abstract void handleRequest(Request request);
}

// Concrete Handlers
public class SupportAgent extends Handler {
    @Override
    public void handleRequest(Request request) {
        if (request.getType() == RequestType.SIMPLE) {
            System.out.println("Support Agent: Handling simple request.");
        } else if (successor != null) {
            successor.handleRequest(request);
        }
    }
}

public class Supervisor extends Handler {
    @Override
    public void handleRequest(Request request) {
        if (request.getType() == RequestType.COMPLEX) {
            System.out.println("Supervisor: Handling complex request.");
        } else if (successor != null) {
            successor.handleRequest(request);
        }
    }
}

public class Manager extends Handler {
    @Override
    public void handleRequest(Request request) {
        if (request.getType() == RequestType.CRITICAL) {
            System.out.println("Manager: Handling critical request.");
        } else {
            System.out.println("Manager: Escalated request to higher authority.");
        }
    }
}

// Request Type
public enum RequestType {
    SIMPLE, COMPLEX, CRITICAL
}

// Request
public class Request {
    private RequestType type;

    public Request(RequestType type) {
        this.type = type;
    }

    public RequestType getType() {
        return type;
    }
}

// Client code
public class Client {
    public static void main(String[] args) {
        Handler agent = new SupportAgent();
        Handler supervisor = new Supervisor();
        Handler manager = new Manager();

        agent.setSuccessor(supervisor);
        supervisor.setSuccessor(manager);

        Request simpleRequest = new Request(RequestType.SIMPLE);
        agent.handleRequest(simpleRequest);

        Request complexRequest = new Request(RequestType.COMPLEX);
        agent.handleRequest(complexRequest);

        Request criticalRequest = new Request(RequestType.CRITICAL);
        agent.handleRequest(criticalRequest);
    }
}

Expanded Explanation

  • Handler (Handler): Abstract class or interface defining how requests should be handled, and maintaining a reference to the next handler.

  • Concrete Handlers (SupportAgent, Supervisor, Manager): Specific handlers in the request processing chain. Each one handles requests of a specific type and delegates others to the next handler.

  • Client (Client): Initiates the requests and sets up the chain of handlers.

Benefits

  • Decoupled Components: Decouples the sender of the request from the receivers by giving more than one object a chance to handle the request.

  • Dynamic Handling: Allows dynamically adding or changing handlers within the chain.

  • Flexibility in Handling: Offers more flexibility in distributing responsibilities among objects.

Drawbacks

  • Processing Time: Handling a request may take longer as it might pass through multiple handlers.

  • Chain Configuration: Misconfiguration of the chain can lead to a request not being handled at all.

Applicability

  • Sequential Processing: Useful when several different objects are expected to process a request in sequence.

  • Conditional Handling: Effective in scenarios where a request can be handled by one of several potential handlers, based on runtime conditions.

Use Cases in Software Development

  • Event Handling in UI Frameworks: Used in graphical user interfaces where an event might be handled by multiple elements in the hierarchy.

  • Approval Processes: Common in workflows where different levels of authorization are required to approve or reject requests.

The Chain of Responsibility Pattern offers a robust framework for processing varied requests within a system

← Back to Library