Java Design Patterns: Understanding the Functionality of the Chain of Responsibility Pattern

Learn how the Chain of Responsibility design pattern in Java enables efficient and decoupled request processing. From the basics to practical implementation using an ATM dispenser system – discover how this pattern improves code organization. Dive into the world of behavioral patterns and optimize your software architecture with this comprehensive overview.

Example of the Chain of Responsibility Pattern in the JDK

An example of the Chain of Responsibility pattern in the JDK is the try-catch block code, where multiple catch blocks exist, each processing a specific exception. When an exception occurs in the try block, it is sent to the first catch block for processing. If the catch block cannot process it, it forwards the request to the next object in the chain, the next catch block. If the last catch block also cannot process it, the exception is passed outside the chain to the calling program.

Example of the Chain of Responsibility Design Pattern

A great example of the Chain of Responsibility pattern is the ATM. The user enters the amount to be dispensed, and the ATM dispenses the amount in defined currency notes such as $50, $20, $10, etc. If the user enters an amount that is not divisible by 10, an error is reported. We will use the Chain of Responsibility pattern to implement this solution. The chain will process the request in the same order as shown in the diagram below. Note that we could implement this solution easily in a single program, but then the complexity would increase, and the solution would be tightly coupled. Therefore, we create a chain of dispensing systems to issue $50, $20, and $10 notes.

Base Classes and Interface for the Chain of Responsibility Design Pattern

We can create a Currency class that stores the amount to be dispensed and is used by the chain implementations. The base interface should have a method to define the next processor in the chain and the method that processes the request. Our ATM dispenser interface looks as follows.


 
package com.journaldev.design.chainofresponsibility;

public class Currency {
    private int amount;
    
    public Currency(int amt){
        this.amount=amt;
    }
    
    public int getAmount(){
        return this.amount;
    }
}


 
package com.journaldev.design.chainofresponsibility;

public interface DispenseChain {
    void setNextChain(DispenseChain nextChain);
    void dispense(Currency cur);
}

Chain Implementations of the Chain of Responsibility Pattern

Various processor classes need to be created that implement the DispenseChain interface and provide the implementation of the dispense methods. As we develop our system to work with three types of currency notes – $50, $20, and $10 – we will create three concrete implementations.


 
package com.journaldev.design.chainofresponsibility;

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

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


 
package com.journaldev.design.chainofresponsibility;

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

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


 
package com.journaldev.design.chainofresponsibility;

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

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

Creating the Chain for the Chain of Responsibility Design Pattern

This is a very important step, and we should create the chain carefully; otherwise, a processor may not receive any requests at all.

 
package com.journaldev.design.chainofresponsibility;

import java.util.Scanner;

public class ATMDispenseChain {
    private DispenseChain c1;

    public ATMDispenseChain() {
        // initialize the chain
        this.c1 = new Dollar50Dispenser();
        DispenseChain c2 = new Dollar20Dispenser();
        DispenseChain c3 = new Dollar10Dispenser();

        // set the chain of responsibility
        c1.setNextChain(c2);
        c2.setNextChain(c3);
    }

    public static void main(String[] args) {
        ATMDispenseChain atmDispenser = new ATMDispenseChain();
        while (true) {
            int amount = 0;
            System.out.println("Enter amount to dispense");
            Scanner input = new Scanner(System.in);
            amount = input.nextInt();
            if (amount % 10 != 0) {
                System.out.println("Amount should be in multiple of 10s.");
                return;
            }
            // process the request
            atmDispenser.c1.dispense(new Currency(amount));
        }
    }
}

Key Points of the Chain of Responsibility Design Pattern

  • The client does not know which part of the chain processes the request, and it sends the request to the first object in the chain.
  • Each object in the chain has its own implementation for processing the request, either completely or partially, or to forward it to the next object in the chain.
  • Each object in the chain should have a reference to the next object in the chain to forward the request.

Examples of the Chain of Responsibility Pattern in the JDK

  • java.util.logging.Logger#log()
  • javax.servlet.Filter#doFilter()

That was all about the Chain of Responsibility design pattern. I hope you enjoyed it and it helped clarify your understanding of this design pattern.

Create a Free Account

Register now and get access to our Cloud Services.

Posts you might be interested in: