Learnitweb

Chain of Responsibility design pattern in Java

Introduction

The Chain of Responsibility (CoR) pattern is a behavioral design pattern that allows multiple objects to handle a request in a chain-like manner, where each handler processes the request or forwards it to the next handler in the chain.

This pattern is commonly used in scenarios like event handling, middleware processing, logging frameworks, authentication, and request validation.

Key Features of Chain of Responsibility

  • Decouples sender and receiver – The sender does not know which handler will process the request.
  • Flexible request processing – The request can be handled at different levels dynamically.
  • Reduces conditional statements – Avoids excessive if-else or switch statements.
  • Easily extendable – New handlers can be added without modifying existing code.

Real-World Analogy

Imagine a customer support system:

  • A junior support agent tries to resolve a customer’s issue.
  • If they can’t resolve it, they escalate it to a supervisor.
  • If still unresolved, the issue goes to a manager.

This escalation process forms a Chain of Responsibility.

Implementing Chain of Responsibility in Java

Step 1: Define an Abstract Handler

The abstract handler contains a reference to the next handler and defines a method for handling requests.

abstract class RequestHandler {
    protected RequestHandler nextHandler; // Reference to the next handler in the chain

    public void setNextHandler(RequestHandler nextHandler) {
        this.nextHandler = nextHandler;
    }

    public void handleRequest(String request) {
        if (nextHandler != null) {
            nextHandler.handleRequest(request); // Pass the request to the next handler
        } else {
            System.out.println("No handler found for: " + request);
        }
    }
}

Step 2: Create Concrete Handlers

Each handler processes a request or forwards it to the next handler.

Handler 1: Authentication Handler

class AuthenticationHandler extends RequestHandler {
    @Override
    public void handleRequest(String request) {
        if ("AUTH".equalsIgnoreCase(request)) {
            System.out.println("Authentication successful.");
        } else {
            System.out.println("Authentication failed. Passing to next handler...");
            super.handleRequest(request);
        }
    }
}

Handler 2: Authorization Handler

class AuthorizationHandler extends RequestHandler {
    @Override
    public void handleRequest(String request) {
        if ("AUTHZ".equalsIgnoreCase(request)) {
            System.out.println("User is authorized.");
        } else {
            System.out.println("Authorization failed. Passing to next handler...");
            super.handleRequest(request);
        }
    }
}

Handler 3: Data Validation Handler

class ValidationHandler extends RequestHandler {
    @Override
    public void handleRequest(String request) {
        if ("VALID".equalsIgnoreCase(request)) {
            System.out.println("Data is valid.");
        } else {
            System.out.println("Invalid data. Passing to next handler...");
            super.handleRequest(request);
        }
    }
}

Step 3: Configure the Chain

Now, we need to set up the handlers in a chain.

public class ChainOfResponsibilityDemo {
    public static void main(String[] args) {
        // Creating handlers
        RequestHandler authHandler = new AuthenticationHandler();
        RequestHandler authzHandler = new AuthorizationHandler();
        RequestHandler validationHandler = new ValidationHandler();

        // Setting up the chain
        authHandler.setNextHandler(authzHandler);
        authzHandler.setNextHandler(validationHandler);

        // Sending requests
        System.out.println("Processing Request: AUTH");
        authHandler.handleRequest("AUTH");

        System.out.println("\nProcessing Request: AUTHZ");
        authHandler.handleRequest("AUTHZ");

        System.out.println("\nProcessing Request: INVALID");
        authHandler.handleRequest("INVALID");
    }
}

Output

Processing Request: AUTH
Authentication successful.

Processing Request: AUTHZ
Authentication failed. Passing to next handler...
User is authorized.

Processing Request: INVALID
Authentication failed. Passing to next handler...
Authorization failed. Passing to next handler...
Invalid data. Passing to next handler...
No handler found for: INVALID

Advantages of Chain of Responsibility Pattern

  • Reduces coupling between sender and receiver.
  • Supports dynamic request handling.
  • Easy to add new handlers.
  • Improves code maintainability.