Chain of Responsibility Design Pattern in Java

A Real-World Example of Loan Approval Process

Introduction

Chain of Responsibility is a behavioural design pattern that allows you to pass requests along a chain of handlers, each handling the request if possible or forwarding it to the next handler in the chain if not. The Chain of Responsibility pattern is designed to decouple the sender and receiver of a request by providing multiple potential receivers, each with its own handler. This pattern is used to achieve loose coupling in software design, where a set of objects is responsible for handling requests.

Example

Let’s consider a real-world example of a bank. When a customer requests a loan, the loan application is processed by several departments within the bank, such as loan verification, credit check, and loan approval. Each of these departments has its own set of rules and policies for processing loan applications. The loan application is passed from one department to another until it is either approved or rejected. This scenario can be modelled using the Chain of Responsibility pattern.

To demonstrate this pattern, we will consider a simplified example of a loan approval process. We will create three classes: Loan, LoanProcessor, and LoanApproval. The loan class will contain the details of a loan application such as loan amount, interest rate, and duration. LoanProcessor class will be the abstract base class for all loan processors, and LoanApproval class will be the concrete class that will approve the loan application.

Here is the Java code for the Loan class:

public class Loan {
    private double amount;
    private double interestRate;
    private int duration;

    public Loan(double amount, double interestRate, int duration) {
        this.amount = amount;
        this.interestRate = interestRate;
        this.duration = duration;
    }

    public double getAmount() {
        return amount;
    }

    public double getInterestRate() {
        return interestRate;
    }

    public int getDuration() {
        return duration;
    }
}

Here is the Java code for the LoanProcessor class:

public abstract class LoanProcessor {
    private LoanProcessor nextProcessor;

    public void setNextProcessor(LoanProcessor nextProcessor) {
        this.nextProcessor = nextProcessor;
    }

    public void processLoan(Loan loan) {
        if (approveLoan(loan)) {
            System.out.println("Loan approved by " + this.getClass().getSimpleName());
        } else if (nextProcessor != null) {
            nextProcessor.processLoan(loan);
        } else {
            System.out.println("Loan cannot be approved");
        }
    }

    protected abstract boolean approveLoan(Loan loan);
}

In the above code, LoanProcessor class is an abstract class that defines the basic structure of the loan processor. It contains a reference to the next processor in the chain, which can be set using the setNextProcessor() method. The processLoan() method takes a Loan object as a parameter and calls the approveLoan() method to check whether the loan can be approved. If the loan is approved, the message is printed, and the processing is stopped. If the loan cannot be approved, and the next processor is not null, the request is forwarded to the next processor in the chain. If there are no more processors, a message is printed saying that the loan cannot be approved.

Here is the Java code for the LoanApproval class:

public class LoanApproval extends LoanProcessor {
    protected boolean approveLoan(Loan loan) {
        if (loan.getAmount() < 50000 && loan.getInterestRate() < 10 && loan.getDuration() < 5) {
            return true;
        } else {
            return false;
        }
    }
}

n the above code, LoanApproval class extends the LoanProcessor class and implements the approveLoan() method to check whether the loan can be approved based on the loan amount, interest rate, and duration. If the loan satisfies the conditions, the loan is approved, and the method returns true. Otherwise, the method returns false, and the request is forwarded to the next processor in the chain.

To test our implementation, we can create an instance of the Loan class and pass it to the LoanProcessor. Here is the Java code for testing our implementation:

public class Main {
    public static void main(String[] args) {
        Loan loan = new Loan(25000, 8, 3);

        LoanProcessor loanProcessor1 = new LoanApproval();
        LoanProcessor loanProcessor2 = new LoanApproval();
        LoanProcessor loanProcessor3 = new LoanApproval();

        loanProcessor1.setNextProcessor(loanProcessor2);
        loanProcessor2.setNextProcessor(loanProcessor3);

        loanProcessor1.processLoan(loan);
    }
}

In the above code, we create an instance of the Loan class and pass it to the LoanProcessor. We create three instances of the LoanApproval class and set the next processor in the chain using the setNextProcessor() method. We then call the processLoan() method on the first processor in the chain.

When we run the above code, the loan is approved by the first LoanApproval processor since the loan amount, interest rate, and duration satisfy the conditions. The output of the above code will be:

Loan approved by LoanApproval

Conclusion

The Chain of Responsibility pattern is a powerful design pattern that allows you to decouple the sender and receiver of a request. It provides a flexible mechanism for processing requests by allowing you to add, remove, or modify handlers dynamically. In this article, we have demonstrated how to implement the Chain of Responsibility pattern using Java. By following this example, you can use the Chain of Responsibility pattern in your own projects to handle complex processing scenarios.