đź“…Week-5 (Day-2) - Understanding the Chain of Responsibility Pattern: Building a Cash Dispenser with UML and Code

Payal KumariPayal Kumari
5 min read

NOTE: - I started my 8-week system design journey with Coder Army. I will be journaling every day, recording what I learn, reflecting on it, and sharing it with my network to help newcomers to system design.

đź’  What is Chain of Responsibility Pattern?

The Chain of Responsibility Pattern allows an object to pass a request along a chain of potential handlers. Each handler decides whether to process the request or pass it to the next one.

(Hindi: Chain of Responsibility Pattern ek aisa behavioral pattern hai jisme request ek chain ke through pass hoti hai. Har node decide karta hai ki request ka response dena hai ya aage bhejna hai.)

📍 Definition: Allow an object to pass a request along a chain of potential handlers. Each handler in the chain either processes the request or passes it to the next handler.

đź’  Real-Life Analogy: Cash Dispenser ATM

Let’s say you want to withdraw ₹3,800 from an ATM. The machine:

  • First tries ₹2000 handler

  • Then ₹500 handler

  • Then ₹100 handler

Each note handler checks: "Can I dispense this amount?" and if yes, it does and passes on the rest.

➡️ This is exactly how Chain of Responsibility works!

(Hindi: ATM jab ₹3800 deta hai, toh pehle ₹2000 ke note, phir ₹500, phir ₹100 ke note nikalta hai — ek chain ke through.)

abstract class MoneyHandler {
    protected MoneyHandler nextHandler;

    public MoneyHandler() {
        this.nextHandler = null;
    }

    public void setNextHandler(MoneyHandler next) {
        this.nextHandler = next;
    }

    public abstract void dispense(int amount);
}

class ThousandHandler extends MoneyHandler {
    private int numNotes;

    public ThousandHandler(int numNotes) {
        this.numNotes = numNotes;
    }

    @Override
    public void dispense(int amount) {
        int notesNeeded = amount / 1000;

        if (notesNeeded > numNotes) {
            notesNeeded = numNotes;
            numNotes = 0;
        } else {
            numNotes -= notesNeeded;
        }

        if (notesNeeded > 0)
            System.out.println("Dispensing " + notesNeeded + " x ₹1000 notes.");

        int remainingAmount = amount - (notesNeeded * 1000);
        if (remainingAmount > 0) {
            if (nextHandler != null) nextHandler.dispense(remainingAmount);
            else {
                System.out.println("Remaining amount of " + remainingAmount + " cannot be fulfilled (Insufficinet fund in ATM)");
            }
        }
    }
}

class FiveHundredHandler extends MoneyHandler {
    private int numNotes;

    public FiveHundredHandler(int numNotes) {
        this.numNotes = numNotes;
    }

    @Override
    public void dispense(int amount) {
        int notesNeeded = amount / 500;

        if (notesNeeded > numNotes) {
            notesNeeded = numNotes;
            numNotes = 0;
        } else {
            numNotes -= notesNeeded;
        }

        if (notesNeeded > 0)
            System.out.println("Dispensing " + notesNeeded + " x ₹500 notes.");

        int remainingAmount = amount - (notesNeeded * 500);
        if (remainingAmount > 0) {
            if (nextHandler != null) nextHandler.dispense(remainingAmount);
            else {
                System.out.println("Remaining amount of " + remainingAmount + " cannot be fulfilled (Insufficinet fund in ATM)");
            }
        }
    }
}

class TwoHundredHandler extends MoneyHandler {
    private int numNotes;

    public TwoHundredHandler(int numNotes) {
        this.numNotes = numNotes;
    }

    @Override
    public void dispense(int amount) {
        int notesNeeded = amount / 200;

        if (notesNeeded > numNotes) {
            notesNeeded = numNotes;
            numNotes = 0;
        } else {
            numNotes -= notesNeeded;
        }

        if (notesNeeded > 0)
            System.out.println("Dispensing " + notesNeeded + " x ₹200 notes.");

        int remainingAmount = amount - (notesNeeded * 200);
        if (remainingAmount > 0) {
            if (nextHandler != null) nextHandler.dispense(remainingAmount);
            else {
                System.out.println("Remaining amount of " + remainingAmount + " cannot be fulfilled (Insufficinet fund in ATM)");
            }
        }
    }
}

class HundredHandler extends MoneyHandler {
    private int numNotes;

    public HundredHandler(int numNotes) {
        this.numNotes = numNotes;
    }

    @Override
    public void dispense(int amount) {
        int notesNeeded = amount / 100;

        if (notesNeeded > numNotes) {
            notesNeeded = numNotes;
            numNotes = 0;
        } else {
            numNotes -= notesNeeded;
        }

        if (notesNeeded > 0)
            System.out.println("Dispensing " + notesNeeded + " x ₹100 notes.");

        int remainingAmount = amount - (notesNeeded * 100);
        if (remainingAmount > 0) {
            if (nextHandler != null) nextHandler.dispense(remainingAmount);
            else {
                System.out.println("Remaining amount of " + remainingAmount + " cannot be fulfilled (Insufficinet fund in ATM)");
            }
        }
    }
}

public class COR {
    public static void main(String[] args) {
        MoneyHandler thousandHandler = new ThousandHandler(3);
        MoneyHandler fiveHundredHandler = new FiveHundredHandler(5);
        MoneyHandler twoHundredHandler = new TwoHundredHandler(10);
        MoneyHandler hundredHandler = new HundredHandler(20);

        thousandHandler.setNextHandler(fiveHundredHandler);
        fiveHundredHandler.setNextHandler(twoHundredHandler);
        twoHundredHandler.setNextHandler(hundredHandler);

        int amountToWithdraw = 4000;

        System.out.println("\nDispensing amount: ₹" + amountToWithdraw);
        thousandHandler.dispense(amountToWithdraw);
    }
}

đź’  Real-World Design Use Case: Logger System

Logging systems also use Chain of Responsibility:

  • 🔹 InfoLogger → Handles general messages

  • 🔸 DebugLogger → Debug info

  • đź”´ ErrorLogger → Logs critical issues

Depending on the log level, the request is passed and handled accordingly.

(Hindi: Jaise system mein different level ke log hotey hain — Info, Debug, Error — toh request ko sahi logger tak pass kiya jata hai.)

📝 Homework: Leave Request System (Employee Leave Flow)

Scenario:

  • 2 Days Leave → Handled by Team Lead

  • 5 Days Leave → Handled by Manager

  • More → Forwarded to Director

interface LeaveApprover {
    void setNext(LeaveApprover next);
    void approveLeave(int days);
}

class TeamLead implements LeaveApprover {
    private LeaveApprover next;
    public void setNext(LeaveApprover next) { this.next = next; }
    public void approveLeave(int days) {
        if (days <= 2) System.out.println("TeamLead approved leave for " + days + " days");
        else next.approveLeave(days);
    }
}

(Hindi: Agar leave 2 din ka hai toh Team Lead approve karega, agar zyada hai toh Manager ya Director approve karega.)

đź’ Benefits of Chain of Responsibility Pattern

  • Decouples sender and receiver

  • Flexible and scalable

  • Easy to add or remove handlers

  • Clean separation of concerns

đź’ Summary

  • Chain of Responsibility kya karta hai?

Request ko ek chain ke through forward karta hai jab tak uska handler mil jaye.

ATM, Logger, Leave Request — sab mein yeh pattern useful hai.

Week - 5 (Day-2) Completed âś… System Design

NOTE : - A big thanks to my mentors Rohit Negi Sir and Aditya Sir for launching this amazing 8-week course absolutely free on YouTube via CoderArmy9 :- youtube.com/@CoderArmy9 . 🙌

👉 Share this blog with your connections! Let’s keep learning, growing, and supporting one another on this journey. 🚀

✍️ Payal Kumari 👩‍💻

Jai Hind 🇮🇳 | #CoderArmy #LearningInPublic #SystemDesign #TechForAll #MentorshipMatters #8weeksLLdChallenge #LowLevelDesign #LLD 👩‍💻

0
Subscribe to my newsletter

Read articles from Payal Kumari directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Payal Kumari
Payal Kumari

I'm a passionate full-stack developer with a strong foundation in the MERN stack—building and maintaining scalable web applications using React.js, Node.js, and Next.js. My journey in open source began with Hacktoberfest 2023, where I made four impactful pull requests that sparked a love for collaborative coding, global learning, and open knowledge sharing. Since then, I’ve contributed to and mentored projects in top open source programs like GSSoC’24, SSOC’24, and C4GT’24. As a Google Gen AI Exchange Hackathon ’24 Finalist and Google’s Women Techmakers (WTM) Ambassador, I’ve been privileged to support diverse communities in building meaningful tech solutions. My work as a Top 50 Mentor for GSSoC ’24 reflects my commitment to nurturing new talent in tech. Beyond development, I serve as a Student Career Guide, Profile Building Expert & Evangelist at Topmate.io, where I conduct workshops, guide students through resume building and career strategy, and help mentees navigate open source and tech careers. Recognized among the Top 5% of mentors and featured on “Topmate Discover,” I take pride in making mentorship accessible and impactful. My technical voice has also been acknowledged by LinkedIn, where I’ve earned the Top Voice badge seven times in domains like web development, programming, and software engineering. In addition, I hold LinkedIn Golden Badges for Research Skills, Interpersonal Skills, Critical Thinking, and Teamwork—signaling a well-rounded approach to both individual contribution and team collaboration. Graduating with an MCA from Chandigarh University in 2023, I’ve continued to fuel my curiosity by writing technical articles and sharing practical MERN stack insights across platforms. Whether it’s building polished UIs, optimizing backend performance, or guiding a mentee through their first pull request, I’m driven by the power of community and continuous learning. Let’s connect! I'm open to collaborations, mentorship, or building something impactful together. Reach out to me at kumaripayal7488@gmail.com or visit my profile on Topmate.io.