00:00

Chain of Responsibility Design Pattern

The Chain of Responsibility is a behavioral design pattern. It allows a request to be passed along a chain of handlers. Each handler decides whether it can handle the request or pass it to the next handler in the chain.

In simple words, instead of sending a request directly to a specific object, the request moves through multiple objects until one of them handles it.

Why Do We Need This Pattern?

This pattern is useful when:

  • Multiple objects can handle the same request
  • You don’t want to tightly couple the sender and receiver
  • You want to add or change handlers without changing existing code

It follows the principle of loose coupling and makes the code more flexible and maintainable.

Real-Life Example

Imagine a customer support system:

  • Level 1 Support handles basic issues
  • Level 2 Support handles technical issues
  • Manager handles critical issues

If Level 1 cannot resolve the issue, it forwards the request to Level 2, and so on.

Structure of Chain of Responsibility

  • Handler – Defines the interface for handling requests
  • Concrete Handlers – Actual classes that handle or forward the request
  • Client – Sends the request to the first handler

Java Example: Approval System

Let’s understand this pattern using a simple loan approval example.

Step 1: Create the Handler Interface


public abstract class LoanApprover {
    protected LoanApprover nextApprover;

    public void setNextApprover(LoanApprover nextApprover) {
        this.nextApprover = nextApprover;
    }

    public abstract void approveLoan(int amount);
}

Step 2: Create Concrete Handlers


public class Manager extends LoanApprover {

    @Override
    public void approveLoan(int amount) {
        if (amount <= 50000) {
            System.out.println("Manager approved the loan of " + amount);
        } else if (nextApprover != null) {
            nextApprover.approveLoan(amount);
        }
    }
}

public class Director extends LoanApprover {

    @Override
    public void approveLoan(int amount) {
        if (amount <= 200000) {
            System.out.println("Director approved the loan of " + amount);
        } else if (nextApprover != null) {
            nextApprover.approveLoan(amount);
        }
    }
}

public class CEO extends LoanApprover {

    @Override
    public void approveLoan(int amount) {
        if (amount <= 1000000) {
            System.out.println("CEO approved the loan of " + amount);
        } else {
            System.out.println("Loan amount too high. Rejected.");
        }
    }
}

Step 3: Client Code


public class ChainOfResponsibilityDemo {

    public static void main(String[] args) {

        LoanApprover manager = new Manager();
        LoanApprover director = new Director();
        LoanApprover ceo = new CEO();

        manager.setNextApprover(director);
        director.setNextApprover(ceo);

        manager.approveLoan(40000);
        manager.approveLoan(150000);
        manager.approveLoan(750000);
    }
}

Output


Manager approved the loan of 40000
Director approved the loan of 150000
CEO approved the loan of 750000

Key Advantages

  • Reduces coupling between sender and receiver
  • Easy to add new handlers
  • Improves code flexibility

Key Disadvantages

  • Request may go unhandled if no handler supports it
  • Debugging can be difficult due to multiple handlers

When to Use Chain of Responsibility

  • When multiple objects can process a request
  • When the exact handler is not known in advance
  • When you want to avoid long if-else chains

Conclusion

The Chain of Responsibility design pattern helps in creating flexible and loosely coupled systems. By passing requests along a chain, it allows the system to decide the correct handler at runtime, making the application easier to extend and maintain.