The Chain of Responsibility pattern is a behavioral pattern that allows a set of objects or handlers to handle a request or message, passing it along to the next object if the current object cannot handle the request. The idea behind the pattern is to decouple the sender of the request from the receiver, and allow multiple objects to handle the request, without knowing which object will handle the request or how many objects will handle it.
In a chain of objects, each object has a reference to the next object in the chain (or null if it is the last object in the chain). When an object receives a request or message, it can do one of three things:
1. Handle the request and return a result.
2. Forward the request to the next object in the chain.
3. Do nothing, if the object cannot handle the request and there is no next object in the chain.
Here’s an example of how the Chain of Responsibility pattern can be used to handle requests in a banking system. Suppose a bank has three different types of accounts: Checking, Savings, and Investment. Each account has its own overdraft limit, and when a customer tries to withdraw money, the bank needs to check if the account has sufficient funds. If not, the bank needs to decide if the customer is eligible for an overdraft, based on the overdraft limit of the account.
To implement this functionality using the Chain of Responsibility pattern, we can create three different handlers: CheckingHandler, SavingsHandler, and InvestmentHandler. Each handler in the chain has a reference to the next handler in the chain (or null if it is the last handler in the chain). The CheckingHandler can handle requests related to checking accounts, and if it cannot handle the request, it forwards it to the SavingsHandler. The SavingsHandler can handle requests related to savings accounts, and if it cannot handle the request, it forwards it to the InvestmentHandler. The InvestmentHandler can handle requests related to investment accounts, and if it cannot handle the request, it does nothing.
Here’s an example implementation of the three handlers:
public interface AccountHandler {
void setNext(AccountHandler handler);
void withdraw(Account account, double amount);
}public class CheckingHandler implements AccountHandler {
private AccountHandler next;
@Override
public void setNext(AccountHandler handler) {
this.next = handler;
}
@Override
public void withdraw(Account account, double amount) {
if (account instanceof CheckingAccount) {
CheckingAccount checkingAccount = (CheckingAccount) account;
double balance = checkingAccount.getBalance();
double overdraftLimit = checkingAccount.getOverdraftLimit();
if (balance + overdraftLimit >= amount) {
checkingAccount.setBalance(balance - amount);
System.out.println("Withdrawal of $" + amount + " from Checking Account successful.");
} else if (next != null) {
next.withdraw(account, amount);
} else {
System.out.println("Withdrawal of $" + amount + " from Checking Account failed. Overdraft limit exceeded.");
}
}
}
}public class SavingsHandler implements AccountHandler {
private AccountHandler next;
@Override
public void setNext(AccountHandler handler) {
this.next = handler;
}
@Override
public void withdraw(Account account, double amount) {
if (account instanceof SavingsAccount) {
SavingsAccount savingsAccount = (SavingsAccount) account;
double balance = savingsAccount.getBalance();
double overdraftLimit = savingsAccount.getOverdraftLimit();
if (balance + overdraftLimit >= amount) {
savingsAccount.setBalance(balance - amount);
System.out.println("Withdrawal of $" + amount + " from Savings Account successful.");
} else if (next != null) {
next.withdraw(account, amount);
} else {
System.out.println("Withdrawal of $" + amount + " from Savings Account failed. Overdraft limit exceeded.");
}
}
}
}public class InvestmentHandler implements AccountHandler {
private AccountHandler next;
@Override
public void setNext(AccountHandler handler) {
this.next = handler;
}
@Override
public void withdraw(Account account, double amount) {
if (account instanceof InvestmentAccount) {
InvestmentAccount investmentAccount = (InvestmentAccount) account;
double balance = investmentAccount.getBalance();
double overdraftLimit = investmentAccount.getOverdraftLimit();
if (balance + overdraftLimit >= amount) {
investmentAccount.setBalance(balance - amount);
System.out.println("Withdrawal of $" + amount + " from Investment Account successful.");
} else if (next != null) {
next.withdraw(account, amount);
} else {
System.out.println("Withdrawal of $" + amount + " from Investment Account failed. Overdraft limit exceeded.");
}
}
}
}In this example, the CheckingHandler is the first handler in the chain, followed by the SavingsHandler and the InvestmentHandler. Each handler checks if it can handle the request based on the type of account, and if it cannot handle the request, it forwards it to the next handler in the chain. If there is no next handler in the chain, it prints an error message indicating that the withdrawal failed.
This implementation of the Chain of Responsibility pattern allows for flexible handling of requests in a banking system, and can be easily extended to account for additional types of accounts or different types of requests.