- C++ Home
- C++ Overview
- C++ Environment Setup
- C++ Basic Syntax
- C++ Comments
- C++ Hello World
- C++ Omitting Namespace
- C++ Tokens
- C++ Constants/Literals
- C++ Keywords
- C++ Identifiers
- C++ Data Types
- C++ Numeric Data Types
- C++ Character Data Type
- C++ Boolean Data Type
- C++ Variable Types
- C++ Variable Scope
- C++ Multiple Variables
- C++ Input Output Operations
- C++ Basic Input/Output
- C++ Cin
- C++ Cout
- C++ Manipulators
- Type System & Data Representation
- C++ Modifier Types
- C++ Storage Classes
- C++ Constexpr Specifier
- C++ Numbers
- C++ Enumeration
- C++ Enum Class
- C++ References
- C++ Date & Time
- C++ Operators
- C++ Operators
- C++ Arithmetic Operators
- C++ Relational Operators
- C++ Logical Operators
- C++ Bitwise Operators
- C++ Assignment Operators
- C++ sizeof Operator
- C++ Conditional Operator
- C++ Comma Operator
- C++ Member Operators
- C++ Casting Operators
- C++ Pointer Operators
- C++ Operators Precedence
- C++ Unary Operators
- C++ Scope Resolution Operator
- C++ Control Statements
- C++ Decision Making
- C++ if Statement
- C++ if else Statement
- C++ Nested if Statements
- C++ switch Statement
- C++ Nested switch Statements
- C++ Loop Types
- C++ while Loop
- C++ for Loop
- C++ do while Loop
- C++ Foreach Loop
- C++ Nested Loops
- C++ Jump Statements
- C++ break Statement
- C++ continue Statement
- C++ goto Statement
- C++ Return Values
- C++ Strings
- C++ Strings
- C++ Loop Through a String
- C++ String Length
- C++ String Concatenation
- C++ String Comparison
- C++ Functions
- C++ Functions
- C++ Multiple Function Parameters
- C++ Recursive Function
- C++ Function Overloading
- C++ Function Overriding
- C++ Default Arguments
- C++ Arrays
- C++ Arrays
- C++ Multidimensional Arrays
- C++ Pointer to an Array
- C++ Passing Arrays to Functions
- C++ Return Array from Functions
- C++ Array Decay
- C++ Structure & Union
- C++ Structures
- C++ Unions
- C++ Class and Objects
- C++ Object Oriented
- C++ Classes & Objects
- C++ Class Member Functions
- C++ Class Access Modifiers
- C++ Static Class Members
- C++ Static Data Members
- C++ Static Member Function
- C++ Inline Functions
- C++ this Pointer
- C++ Friend Functions
- C++ Pointer to Classes
- C++ Constructors
- C++ Constructor & Destructor
- C++ Default Constructors
- C++ Parameterized Constructors
- C++ Copy Constructor
- C++ Constructor Overloading
- C++ Constructor with Default Arguments
- C++ Delegating Constructors
- C++ Constructor Initialization List
- C++ Dynamic Initialization Using Constructors
- C++ Destructors
- C++ Virtual Destructor
- C++ Inheritance
- C++ Inheritance
- C++ Multiple Inheritance
- C++ Multilevel Inheritance
- C++ Object-oriented
- C++ Overloading
- C++ Polymorphism
- C++ Abstraction
- C++ Encapsulation
- C++ Interfaces
- C++ Virtual Function
- C++ Pure Virtual Functions & Abstract Classes
- C++ Override Specifiers
- C++ Final Specifiers
- C++ Design Patterns
- C++ Creational Design Patterns
- C++ Singleton Design Pattern
- C++ Factory Method Design Pattern
- C++ Abstract Factory Pattern
- C++ Prototype Design Pattern
- C++ Structural Design Patterns
- C++ Facade Design Pattern
- C++ File Handling
- C++ Files and Streams
- C++ Reading From File
- C++ Advanced
- C++ Exception Handling
- C++ Dynamic Memory
- C++ Move Semantics
- C++ Namespaces
- C++ Templates
- C++ Preprocessor
- C++ Signal Handling
- C++ Multithreading
- C++ Web Programming
- C++ Socket Programming
- C++ Concurrency
- C++ Advanced Concepts
- C++ Lambda Expression
- C++ nullptr
- C++ unordered_multiset
- C++ Chain of Responsibility
- C++ Structural Design Patterns
- C++ Adapter Pattern
- C++ Bridge Pattern
- C++ Composite Pattern
- C++ Decorator Pattern
- C++ Flyweight Pattern
- C++ Command Pattern
- C++ Proxy Pattern
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.
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++:
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.