Thursday, 30 November 2017

Design Patterns | Chain of Responsibility

Decoupling is one of the prominent mantras in software engineering.

Chain of responsibility helps to decouple sender of a request and receiver of the request with some trade-offs.

Using Chain of responsibility, a sender sends a request to a chain of objects, where the objects in the chain decide themselves who to serve the request. If an object in the chain decides not to serve the request, it forwards the request to the next object in the chain.

Real life example
It is similar to 'passing the question in a quiz scenario'.

When the quiz master asks a question to a person, if he doesn’t know the answer, he passes the question to next person and so on. When one person answers the question, the passing flow stops. Sometimes, the passing might reach the last person and still, nobody gives the answer.

Participants
The classes and objects participating in this pattern are:


Handler (Approver)
Defines an interface for handling the requests and we can implement the successor link.

ConcreteHandler (Successor)
Each Concrete handler handles requests for which it is responsible and can access its successor.
If a ConcreteHandler can handle the request, it does so; otherwise, it forwards the request to its successor.

Client
It initiates the request to a ConcreteHandler object of the chain.

ATM dispense machine: A chain of responsibility example
This is one of the common examples of Chain of Responsibility pattern. The user enters the amount to dispense it and the machine dispenses amount in terms of defined currency bills such as Rs. 1000, 500, 100. If the user enters an amount that is not multiples of 100, ATM displays the error message.

Chain of Responsibility pattern used to implement this solution and dispenser handler called from higher to lower amount dispenser.

Currency.java
public class Currency {
     private int amount;

     public Currency(int amt){
           this.amount = amt;
     }

     public int getAmount(){
           return this.amount;
     }
}

DispenseChain.java
public interface DispenseChain {

     void setNextChain(DispenseChain nextChain);
    
     void dispense(Currency cur);
}

Rupees1000Dispenser.java
public class Rupees1000Dispenser implements DispenseChain {

     private DispenseChain chain;
    
     @Override
     public void setNextChain(DispenseChain nextChain) {
           this.chain=nextChain;
     }

     @Override
     public void dispense(Currency cur) {
           if(cur.getAmount() >= 1000){
                int num = cur.getAmount()/1000;
                int remainder = cur.getAmount() % 1000;
                System.out.println("Dispensing "+num+" Rs. 1000 note");
                if(remainder !=0) this.chain.dispense(new Currency(remainder));
           } else {
                this.chain.dispense(cur);
           }
     }
}

Rupees500Dispenser.java
public class Rupees500Dispenser implements DispenseChain{

     private DispenseChain chain;

     @Override
     public void setNextChain(DispenseChain nextChain) {
           this.chain=nextChain;
     }

     @Override
     public void dispense(Currency cur) {
           if(cur.getAmount() >= 500){
                int num = cur.getAmount()/500;
                int remainder = cur.getAmount() % 500;
                System.out.println("Dispensing "+num+" Rs. 500 note");
                if(remainder !=0) this.chain.dispense(new Currency(remainder));
           } else {
                this.chain.dispense(cur);
           }
     }
}

Rupees100Dispenser.java
public class Rupees100Dispenser implements DispenseChain {

     private DispenseChain chain;
    
     @Override
     public void setNextChain(DispenseChain nextChain) {
           this.chain=nextChain;
     }

     @Override
     public void dispense(Currency cur) {
           if(cur.getAmount() >= 100) {
                int num = cur.getAmount()/100;
                int remainder = cur.getAmount() % 100;
                System.out.println("Dispensing "+num+" Rs. 100 note");
                if(remainder !=0) this.chain.dispense(new Currency(remainder));
           } else {
                this.chain.dispense(cur);
           }
     }

}

ATMDispenseChain.java
public class ATMDispenseChain {

     private DispenseChain chainStart;

     public ATMDispenseChain() {
          
           // initialize the chain
           this.chainStart = new Rupees1000Dispenser();
           DispenseChain chain500 = new Rupees500Dispenser();
           DispenseChain chain100 = new Rupees100Dispenser();

           // set the chain of responsibility
           chainStart.setNextChain(chain500);
           chain500.setNextChain(chain100);
     }
    
     public DispenseChain getChainStart() {
           return chainStart;
     }
}

TestChainPattern.java
import java.util.Scanner;

public class TestChainPattern {

     public static void main(String[] args) {
           ATMDispenseChain atmDispenser = new ATMDispenseChain();
           int amount = 0;
           System.out.println("Enter amount to dispense");

           Scanner input = new Scanner(System.in);
           amount = input.nextInt();

           if (amount % 100 != 0) {
                System.out.println("Amount should be in multiple of 100s.");
                return;
           }

           // process the request
           atmDispenser.getChainStart().dispense(new Currency(amount));

           input.close();
     }
}

Chain of Responsibility Pattern Examples
ATM dispense machine.
Struts interceptors.

Usage of Chain of Responsibility Pattern in JDK
java.util.logging.Logger#log()
javax.servlet.Filter#doFilter()


No comments:

Post a Comment

Related Posts Plugin for WordPress, Blogger...