Demystifying Design Patterns: Chain of Responsibility Design Pattern

21 Sep
  1. Demystifying Design Patterns: Singleton Design Pattern
  2. Demystifying Design Patterns: Factory Method Design Pattern
  3. Demystifying Design Patterns: Abstract Factory Design Pattern
  4. Demystifying Design Patterns: Builder Design Pattern
  5. Demystifying Design Patterns: Prototype Design Pattern
  6. Demystifying Design Patterns: Adapter Design Pattern
  7. Demystifying Design Patterns: Bridge Design Pattern
  8. Demystifying Design Patterns: Composite Design Pattern
  9. Demystifying Design Patterns: Decorator Design Pattern
  10. Demystifying Design Patterns: Proxy Design Pattern
  11. Demystifying Design Patterns: Observer Design Pattern
  12. Demystifying Design Patterns: Strategy Design Pattern
  13. Demystifying Design Patterns: Command Design Pattern
  14. Demystifying Design Patterns: State Design Pattern
  15. Demystifying Design Patterns: Chain of Responsibility Design Pattern
  16. Demystifying Design Patterns: Visitor Design Pattern
  17. Demystifying Design Patterns: Template Method Design Pattern

Welcome to the 15th installment of our series on Demystifying Design Patterns! In this article, we delve deep into the Chain of Responsibility Design Pattern. This behavioral pattern offers an elegant solution for processing requests or tasks through a chain of handlers. Join us on this journey as we explore the intricacies of this pattern, its practical applications, real-life examples, and provide comprehensive code implementations in Java, C#, and Python. 

Understanding the Chain of Responsibility Design Pattern

The Chain of Responsibility Design Pattern is a behavioral pattern that specializes in managing sequential processing of requests or tasks. It constructs a chain of handler objects, each having the capability to either handle a request or pass it to the next handler in the chain. This pattern encourages loose coupling between request senders and receivers, offering flexibility and extensibility in request processing.

Key Components: Handlers and Requests

To grasp the Chain of Responsibility Pattern, let’s dissect its key components:

– Handler: Handlers are distinct objects, each responsible for processing a specific type of request or task. These handlers are interconnected to form a chain. A handler can decide whether to process a request or delegate it further down the chain.

– Request: Requests represent the tasks or data that require processing. Handlers inspect incoming requests and make a determination—either they handle the request or they pass it along the chain for potential handling by another handler.

Chain of Responsibility vs. Other Patterns

While the Chain of Responsibility Pattern shares similarities with other design patterns like the Command Pattern or the Decorator Pattern, it serves a unique purpose:

– Command Pattern: The Command Pattern emphasizes encapsulating a request as an object with an associated action to be executed. Conversely, the Chain of Responsibility deals with orchestrating the sequential processing of requests through a chain of handlers.

– Decorator Pattern: The Decorator Pattern focuses on dynamically adding responsibilities to objects. It primarily extends an object’s functionality. In contrast, the Chain of Responsibility is all about the sequential processing of requests, with each handler potentially acting on the request.

Real-Life Examples

Example 1: Approval Workflow

Consider an approval workflow system used in organizations. Documents or requests often require approval from multiple levels of management. In this scenario, each manager in the hierarchy corresponds to a handler in the chain. As a document progresses through the chain, each manager decides whether to approve it or pass it up for further review. This application of the Chain of Responsibility Pattern streamlines approval processes.

Example 2: Logging Mechanism

Logging mechanisms frequently involve different loggers responsible for handling logs of varying severity levels. Loggers can be organized into a chain, where each logger decides whether to process the log entry or pass it down the chain for further handling. This pattern offers a flexible approach to managing log data.

Example 3: Customer Support Ticket Routing

In customer support ticketing systems, efficient routing is essential. The Chain of Responsibility Pattern can be employed to route tickets based on criteria like the nature of the issue or the customer’s location. Each support team handler examines the ticket, determines if they can address it, and if not, passes it along to the next team. This results in streamlined ticket management.

Code Examples

Let’s solidify our understanding of the Chain of Responsibility Pattern with comprehensive code examples in Java, C#, and Python. These implementations will illustrate how to construct a chain of handlers and process requests seamlessly through the chain.

example-chain-of-responsibility-design-patterns

Java Example:
// (Java code example illustrating the Chain of Responsibility Pattern)
import java.util.Scanner;

// Handler interface
interface Handler {
    void handleRequest(String request);
    void setNextHandler(Handler nextHandler);
}

// Concrete handlers
class ConcreteHandlerA implements Handler {
    private Handler nextHandler;

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

    public void handleRequest(String request) {
        if (request.equalsIgnoreCase("A")) {
            System.out.println("Handled by ConcreteHandlerA");
        } else if (nextHandler != null) {
            nextHandler.handleRequest(request);
        } else {
            System.out.println("Request cannot be handled.");
        }
    }
}

class ConcreteHandlerB implements Handler {
    private Handler nextHandler;

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

    public void handleRequest(String request) {
        if (request.equalsIgnoreCase("B")) {
            System.out.println("Handled by ConcreteHandlerB");
        } else if (nextHandler != null) {
            nextHandler.handleRequest(request);
        } else {
            System.out.println("Request cannot be handled.");
        }
    }
}

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

        handlerA.setNextHandler(handlerB);

        Scanner scanner = new Scanner(System.in);
        while (true) {
            System.out.print("Enter a request (A/B): ");
            String request = scanner.nextLine();

            if (request.equalsIgnoreCase("exit")) {
                break;
            }

            handlerA.handleRequest(request);
        }
    }
}
C# Example:
// (C# code example illustrating the Chain of Responsibility Pattern)
using System;

// Handler interface
interface Handler {
    void HandleRequest(string request);
    void SetNextHandler(Handler nextHandler);
}

// Concrete handlers
class ConcreteHandlerA : Handler {
    private Handler nextHandler;

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

    public void HandleRequest(string request) {
        if (request.Equals("A", StringComparison.OrdinalIgnoreCase)) {
            Console.WriteLine("Handled by ConcreteHandlerA");
        } else if (nextHandler != null) {
            nextHandler.HandleRequest(request);
        } else {
            Console.WriteLine("Request cannot be handled.");
        }
    }
}

class ConcreteHandlerB : Handler {
    private Handler nextHandler;

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

    public void HandleRequest(string request) {
        if (request.Equals("B", StringComparison.OrdinalIgnoreCase)) {
            Console.WriteLine("Handled by ConcreteHandlerB");
        } else if (nextHandler != null) {
            nextHandler.HandleRequest(request);
        } else {
            Console.WriteLine("Request cannot be handled.");
        }
    }
}

class Client {
    static void Main(string[] args) {
        Handler handlerA = new ConcreteHandlerA();
        Handler handlerB = new ConcreteHandlerB();

        handlerA.SetNextHandler(handlerB);

        while (true) {
            Console.Write("Enter a request (A/B): ");
            string request = Console.ReadLine();

            if (request.Equals("exit", StringComparison.OrdinalIgnoreCase)) {
                break;
            }

            handlerA.HandleRequest(request);
        }
    }
}
Python Example:
# (Python code example illustrating the Chain of Responsibility Pattern)
# Handler interface
class Handler:
    def handle_request(self, request):
        pass

    def set_next_handler(self, next_handler):
        pass

# Concrete handlers
class ConcreteHandlerA(Handler):
    def __init__(self):
        self.next_handler = None

    def set_next_handler(self, next_handler):
        self.next_handler = next_handler

    def handle_request(self, request):
        if request.lower() == 'a':
            print("Handled by ConcreteHandlerA")
        elif self.next_handler is not None:
            self.next_handler.handle_request(request)
        else:
            print("Request cannot be handled.")

class ConcreteHandlerB(Handler):
    def __init__(self):
        self.next_handler = None

    def set_next_handler(self, next_handler):
        self.next_handler = next_handler

    def handle_request(self, request):
        if request.lower() == 'b':
            print("Handled by ConcreteHandlerB")
        elif self.next_handler is not None:
            self.next_handler.handle_request(request)
        else:
            print("Request cannot be handled.")

if __name__ == "__main__":
    handler_a = ConcreteHandlerA()
    handler_b = ConcreteHandlerB()

    handler_a.set_next_handler(handler_b)

    while True:
        request = input("Enter a request (A/B): ").strip()
        if request.lower() == 'exit':
            break
        handler_a.handle_request(request)

Conclusion

The Chain of Responsibility Design Pattern is a robust tool for orchestrating the sequential processing of requests or tasks. By creating a chain of handlers, you can design systems that efficiently handle diverse requests while maintaining modularity and flexibility. In this article, we delved into the core concepts of the pattern, explored real-world applications, and provided extensive code examples in Java, C#, and Python.

With the Chain of Responsibility Pattern at your disposal, you can confidently design systems that gracefully handle a variety of tasks, ensuring clean separation of concerns and maintainability. Stay tuned for the next installment in our Demystifying Design Patterns series, where we unravel yet another pattern’s mysteries!



Leave a Reply

Your email address will not be published. Required fields are marked *