Chain of Responsibility Design Pattern Explained πŸ”„ | LambdaTest

:rocket: New Video Alert! :movie_camera:

Dive into the world of design patterns with our latest video on the Chain of Responsibility Design Pattern. Learn how to streamline your request-handling process efficiently. Don’t miss out on mastering this powerful design pattern. Watch now and elevate your coding skills! :link:

#DesignPatterns #ChainOfResponsibility #LearnCoding

The Chain of Responsibility design pattern allows an object to pass a request along a chain of potential handlers until the request is handled. This pattern decouples the sender and receiver, promoting loose coupling in the system.

To understand the Chain of Responsibility design pattern, let us take an example.

abstract class Handler {
    protected Handler nextHandler;

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

    public abstract void handleRequest(String request);
}

class ConcreteHandlerA extends Handler {
    @Override
    public void handleRequest(String request) {
        if (request.equals("RequestA")) {
            System.out.println("Handler A processed RequestA");
        } else if (nextHandler != null) {
            nextHandler.handleRequest(request);
        }
    }
}

class ConcreteHandlerB extends Handler {
    @Override
    public void handleRequest(String request) {
        if (request.equals("RequestB")) {
            System.out.println("Handler B processed RequestB");
        } else if (nextHandler != null) {
            nextHandler.handleRequest(request);
        }
    }
}

public class Main {
    public static void main(String[] args) {
        Handler handlerA = new ConcreteHandlerA();
        Handler handlerB = new ConcreteHandlerB();

        handlerA.setNextHandler(handlerB);

        handlerA.handleRequest("RequestB");
    }
}

mple, ConcreteHandlerA and ConcreteHandlerB are part of a chain. If handlerA cannot process the request, it passes it to handlerB. When handlerA.handleRequest("RequestB") is called, ConcreteHandlerB processes the request.

The Chain of Responsibility pattern can be extended to multiple handlers, creating a more flexible and dynamic chain. Each handler can decide whether to process the request or pass it to the next handler in the chain.

abstract class Logger {
    public static int INFO = 1;
    public static int DEBUG = 2;
    public static int ERROR = 3;

    protected int level;
    protected Logger nextLogger;

    public void setNextLogger(Logger nextLogger) {
        this.nextLogger = nextLogger;
    }

    public void logMessage(int level, String message) {
        if (this.level <= level) {
            write(message);
        }
        if (nextLogger != null) {
            nextLogger.logMessage(level, message);
        }
    }

    abstract protected void write(String message);
}

class InfoLogger extends Logger {
    public InfoLogger(int level) {
        this.level = level;
    }

    @Override
    protected void write(String message) {
        System.out.println("Info Logger: " + message);
    }
}

class ErrorLogger extends Logger {
    public ErrorLogger(int level) {
        this.level = level;
    }

    @Override
    protected void write(String message) {
        System.out.println("Error Logger: " + message);
    }
}

class DebugLogger extends Logger {
    public DebugLogger(int level) {
        this.level = level;
    }

    @Override
    protected void write(String message) {
        System.out.println("Debug Logger: " + message);
    }
}

public class Main {
    private static Logger getChainOfLoggers() {
        Logger errorLogger = new ErrorLogger(Logger.ERROR);
        Logger debugLogger = new DebugLogger(Logger.DEBUG);
        Logger infoLogger = new InfoLogger(Logger.INFO);

        errorLogger.setNextLogger(debugLogger);
        debugLogger.setNextLogger(infoLogger);

        return errorLogger;
    }

    public static void main(String[] args) {
        Logger loggerChain = getChainOfLoggers();

        loggerChain.logMessage(Logger.INFO, "This is an information.");
        loggerChain.logMessage(Logger.DEBUG, "This is a debug level information.");
        loggerChain.logMessage(Logger.ERROR, "This is an error information.");
    }
}

In this extended example, we have InfoLogger, DebugLogger, and ErrorLogger. Each logger handles messages at different levels. The chain of loggers processes messages from the highest to the lowest level of importance.

Real-World Use Case Example : A real-world application of the Chain of Responsibility pattern can be seen in handling customer support requests. Different support levels (e.g., Tier 1, Tier 2, Tier 3) can be modeled as handlers in a chain.

abstract class SupportHandler {
    protected SupportHandler nextHandler;

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

    public abstract void handleRequest(String request);
}

class Tier1Support extends SupportHandler {
    @Override
    public void handleRequest(String request) {
        if (request.equals("BasicIssue")) {
            System.out.println("Tier 1 Support handling basic issue");
        } else if (nextHandler != null) {
            nextHandler.handleRequest(request);
        }
    }
}

class Tier2Support extends SupportHandler {
    @Override
    public void handleRequest(String request) {
        if (request.equals("IntermediateIssue")) {
            System.out.println("Tier 2 Support handling intermediate issue");
        } else if (nextHandler != null) {
            nextHandler.handleRequest(request);
        }
    }
}

class Tier3Support extends SupportHandler {
    @Override
    public void handleRequest(String request) {
        if (request.equals("ComplexIssue")) {
            System.out.println("Tier 3 Support handling complex issue");
        } else if (nextHandler != null) {
            nextHandler.handleRequest(request);
        }
    }
}

public class Main {
    public static void main(String[] args) {
        SupportHandler tier1 = new Tier1Support();
        SupportHandler tier2 = new Tier2Support();
        SupportHandler tier3 = new Tier3Support();

        tier1.setNextHandler(tier2);
        tier2.setNextHandler(tier3);

        tier1.handleRequest("IntermediateIssue");
    }
}

In this real-world scenario, Tier1Support, Tier2Support, and Tier3Support handle different levels of customer issues.

The chain starts with tier1, which passes the request to tier2 if it cannot handle it. This way, issues are escalated to the appropriate support level.