Chain of Responsibility in C++



The Chain of Responsibility pattern is a behavioral design pattern which allows you pass a object of a request through a chain of potential handlers until one of them handles the request. This pattern decouples the sender of a request from its receiver by giving multiple objects a chance to handle the request.

In this pattern, each handler in the chain has a reference to the next handler. When a request is received, the handler decides either to process the request or to pass it to the next handler in the chain. This continues until a handler processes the request or the end of the chain is reached.

Chain of Responsibility Pattern Diagram

Components of the Chain of Responsibility Pattern

There are three main components in the Chain of Responsibility pattern, we have listed them below −

  • Handler − It can be an abstract class or it may be an interface which defines a method for handling requests and a method for setting the next handler in the chain.
  • Concrete Handler − These are the classes that implement the handler interface and provide specific implementations for handling requests. Each concrete handler decides whether to process the request or pass it to the next handler.
  • Client − The client is responsible for creating the chain of handlers and initiating the request processing by sending the request to the first handler in the chain.

Implementation of the Chain of Responsibility Pattern

Now, let's implement the Chain of Responsibility pattern in C++.

In this example, we will take an real-world scenario where, we have a support ticket system. The tickets can be handled by different levels of support staff based on the priority and complexity of the issue.

Steps to Implement the Chain of Responsibility Pattern in C++

Following are the steps to implement the Chain of Responsibility pattern in C++:

Steps to Implement Chain of Responsibility Pattern

C++ Implementation of Chain of Responsibility Pattern

Here is a simple implementation of the Chain of Responsibility pattern in C++ of the support ticket system −

#include <iostream>
#include <string>
using namespace std;

// Abstract Handler
class SupportHandler {
protected:
   SupportHandler* nextHandler;
public:
   SupportHandler() : nextHandler(nullptr) {}
   void setNextHandler(SupportHandler* handler) {
      nextHandler = handler;
   }
   virtual void handleRequest(const string& issue, int priority) = 0;
};

// Concrete Handler: Level 1 Support
class Level1Support : public SupportHandler {
public:
   void handleRequest(const string& issue, int priority) override {
      if (priority == 1) {
         cout << "Level 1 Support handled the issue: " << issue << endl;
      } else if (nextHandler) {
         nextHandler->handleRequest(issue, priority);
      }
   }
};

// Concrete Handler: Level 2 Support
class Level2Support : public SupportHandler {
public:
   void handleRequest(const string& issue, int priority) override {
      if (priority == 2) {
         cout << "Level 2 Support handled the issue: " << issue << endl;
      } else if (nextHandler) {
         nextHandler->handleRequest(issue, priority);
      }
   }
};

// Concrete Handler: Level 3 Support
class Level3Support : public SupportHandler {
public:
   void handleRequest(const string& issue, int priority) override {
      if (priority == 3) {
         cout << "Level 3 Support handled the issue: " << issue << endl;
      } else if (nextHandler) {
         nextHandler->handleRequest(issue, priority);
      }
   }
};

int main() {
   // Create handlers
   Level1Support level1;
   Level2Support level2;
   Level3Support level3;

   // Set up the chain of responsibility
   level1.setNextHandler(&level2);
   level2.setNextHandler(&level3);

   // Create some support requests
   level1.handleRequest("Password reset", 1);
   level1.handleRequest("Software installation", 2);
   level1.handleRequest("System crash", 3);
   level1.handleRequest("Unknown issue", 4); // No handler for this priority

   return 0;
}

In this example, we have defined an abstract handler class SupportHandler with a method to handle requests and a method to set the next handler. We then created three concrete handlers: Level1Support, Level2Support, and Level3Support, each responsible for handling requests of different priority levels.

In the main function, we created instances of each handler and set up the chain of responsibility. Finally, we created some support requests with different priority levels to demonstrate how the requests are handled by the appropriate handlers in the chain.

Pros and Cons of Chain of Responsibility Pattern

Here are some pros and cons of using the Chain of Responsibility pattern −

Pros Cons
Makes it easy to separate who sends a request from who handles it Can be tricky to figure out where a request was handled
Lets you add or remove support levels without much hassle If the chain gets too long, it might slow things down
Simple to organize and keep your code tidy Some requests might not get handled at all
You can change the order of handlers whenever you need It’s not always obvious how the request moves through the chain
Each handler can focus on just one thing You need to plan carefully how handlers work together
You can reuse the same handler in different chains Having lots of handlers might use more memory

Real-World Examples of Chain of Responsibility Pattern

Here are some real-world examples where the Chain of Responsibility pattern is commonly used −

  • Event Handling in Apps − When you click a button or press a key, the event travels through a series of handlers. The first one that knows what to do with it takes care of it, just like passing a message down a line until someone responds.
  • Logging − When something happens in a program, a log message might go through several loggers—one for errors, one for warnings, one for info—until it finds the right place to be recorded.
  • Customer Support − If you’ve ever contacted customer support, you know your question might get passed from one person to another until someone can actually help you. That’s the chain of responsibility in action.
  • Web Requests − When you visit a website, your request can go through different steps—like checking if you’re logged in, filtering out bad requests, or adding extra info—before it gets to the part that shows you the page.

Conclusion

In this chapter, we’ve seen what the Chain of Responsibility pattern is, how it works, and how to use it in C++. It’s a handy way to keep your code flexible and easy to maintain by letting requests find the right handler without everyone needing to know about each other.

Advertisements