Chain of Responsibility Pattern

The Chain of Responsibility Pattern is a behavioral design pattern that allows multiple objects to handle a request in a chain-like manner.

When a request is made, it is passed through a series of handler objects until one of them handles the request.

The chain can be dynamically modified during runtime, and each handler decides whether to process the request or pass it to the next handler in the chain.

Chain of Responsibility pattern Example:

Imagine a customer support system where customer complaints are handled through different levels. The complaint is first sent to the junior representative, and if they can’t resolve it, it’s forwarded to the senior representative, and so on, until the complaint is resolved.

Chain of Responsibility pattern C++ Example:

Let’s demonstrate the Chain of Responsibility Pattern with a simple example of processing purchase requests. We’ll have different managers responsible for approving requests based on the purchase amount.

Chain of Responsibility pattern Code:

#include <iostream>

// Handler Interface
class Approver {
protected:
    Approver* successor;

public:
    void setSuccessor(Approver* successor) {
        this->successor = successor;
    }

    virtual void processRequest(int amount) = 0;
};

// Concrete Handlers
class Manager : public Approver {
public:
    void processRequest(int amount) override {
        if (amount <= 1000) {
            std::cout << "Manager: Approved the purchase request of $" << amount << std::endl;
        } else if (successor) {
            successor->processRequest(amount);
        } else {
            std::cout << "Manager: Unable to process the purchase request." << std::endl;
        }
    }
};

class Director : public Approver {
public:
    void processRequest(int amount) override {
        if (amount <= 5000) {
            std::cout << "Director: Approved the purchase request of $" << amount << std::endl;
        } else if (successor) {
            successor->processRequest(amount);
        } else {
            std::cout << "Director: Unable to process the purchase request." << std::endl;
        }
    }
};

class VicePresident : public Approver {
public:
    void processRequest(int amount) override {
        if (amount <= 10000) {
            std::cout << "Vice President: Approved the purchase request of $" << amount << std::endl;
        } else if (successor) {
            successor->processRequest(amount);
        } else {
            std::cout << "Vice President: Unable to process the purchase request." << std::endl;
        }
    }
};

int main() {
    Approver* manager = new Manager();
    Approver* director = new Director();
    Approver* vicePresident = new VicePresident();

    manager->setSuccessor(director);
    director->setSuccessor(vicePresident);

    // Sample purchase requests
    manager->processRequest(500);
    manager->processRequest(2500);
    manager->processRequest(7500);
    manager->processRequest(15000);

    delete manager;
    delete director;
    delete vicePresident;

    return 0;
}

Output:

Manager: Approved the purchase request of $500
Director: Approved the purchase request of $2500
Vice President: Approved the purchase request of $7500
Vice President: Unable to process the purchase request.

Chain of Responsibility pattern Explanation of the Code:
In this example, we have four main components:

  1. Approver is the handler interface that declares the processRequest method for handling purchase requests.
  2. Manager, Director, and VicePresident are concrete handlers that implement Approver. Each handler checks if it can approve the purchase request based on the purchase amount. If it can’t, the request is passed to the next handler in the chain (successor).
  3. In the main function, we create instances of the concrete handlers (Manager, Director, and VicePresident) and set their successors accordingly. The request is then passed to the manager, and if the manager can’t handle it, it is passed to the director, and so on, until it is either approved or rejected.

Applications of Chain of Responsibility Pattern:

  1. Request processing: It is commonly used for processing and handling requests in web servers, middleware, or event-driven systems.
  2. Logging systems: It can be used to filter and process log messages based on different logging levels or categories.

Chain of Responsibility pattern Pros and Cons:

Chain of Responsibility pattern Pros:

  • Decouples sender and receiver: The pattern allows multiple objects to handle a request without requiring the sender to know the exact receiver.
  • Flexible chain modification: Handlers can be added or removed at runtime, allowing dynamic changes to the chain.
  • Simplifies the client code: The client doesn’t need to know how the request is processed and can simply send the request to the first handler in the chain.

Chain of Responsibility pattern Cons:

  • Potential inefficiency: In some cases, the request may go through the entire chain before being handled, which may be less efficient than direct handling.
  • Complexity: As the number of handlers increases, the chain can become more complex and difficult to manage.
  • Handling guarantee: There is no guarantee that a request will be handled at any point in the chain, which may result in unhandled requests.