How to Create Chain in Chain of Responsibility Design Pattern?
In modern software development, design patterns are important in structuring code and making it easy to maintain. One such design pattern is the Chain of Responsibility Design Pattern. Whether you're working with C++ or any other object-oriented language, the Chain of Responsibility (CoR) pattern offers a way to handle requests through a series of handlers, each capable of processing or passing the request along.
In this article, we'll explore how to create a chain in the Chain of Responsibility Design Pattern, particularly focusing on C++ programming. We'll walk through an implementation, discuss how it works, and explain why it’s useful when flexible request-handling logic is needed.
You might also want to read about Decorator Design Pattern. Designing Design Patterns with C#
What is the Chain of Responsibility Design Pattern?
The Chain of Responsibility Design Pattern is a behavioral design pattern that allows an object to pass a request through a chain of potential handlers. Once the chain is established each handler processes the request or passes it to the next handler in the chain. This pattern promotes loose coupling between the sender and the receiver of a request, allowing modification to the chain's structure without impacting the client code.
In a typical C++ coding scenario, the Chain of Responsibility can be visualized as a linked list where each node (or handler) decides whether it can process the request. If it can process, it goes on to process the request. If not, it passes the request to the next node in the chain.
When to Use the Chain of Responsibility Pattern
The Chain of Responsibility pattern is useful when:
- You want to decouple request senders from receivers.
- Multiple objects can handle a request, but you don’t know
which one will handle it in advance.
- You need to add flexibility to the request-handling structure without changing the client's code.
For example, in a C++ software application where multiple services can process a user action—such as logging in or performing validation—this pattern can simplify your architecture(like we are doing as part of simplifyyourday 😊) and reduce tight coupling.
Step-by-Step: Creating the Chain in C++
Let’s take a look at how to implement the Chain of Responsibility in C++. Imagine we are building a logging system where different log levels (Info, Warning, Error) must be processed by different handlers.
#include <iostream>
#include <vector>
#include <ctime>
using namespace std;
class Base
{
Base
*next; // 1. "next" pointer in the base class
public:
Base()
{
next
= 0;
}
void
setNext(Base *n)
{
next
= n;
}
void
add(Base *n)
{
if
(next)
next->add(n);
else
next
= n;
}
// 2.
The "chain" method in the base class always delegates to the next obj
virtual void
handle(int i)
{
next->handle(i);
}
};
Concrete Handlers
Now we create the specific handlers that will process
various log levels:
class Handler1 : public Base
{
public:
/*virtual*/void
handle(int i)
{
if
(rand() % 3)
{
//
3. Don't handle requests 3 times out of 4
cout
<< "H1 passed " << i << " ";
Base::handle(i);
// 3. Delegate to the base class
}
else
cout
<< "H1 handled " << i << " ";
}
};
class Handler2 : public Base
{
public:
/*virtual*/void
handle(int i)
{
if
(rand() % 3)
{
cout
<< "H2 passed " << i << " ";
Base::handle(i);
}
else
cout
<< "H2 handled " << i << " ";
}
};
class Handler3 : public Base
{
public:
/*virtual*/void
handle(int i)
{
if
(rand() % 3)
{
cout
<< "H3 passed " << i << " ";
Base::handle(i);
}
else
cout
<< "H3 handled " << i << " ";
}
};
Each handler specializes in processing a specific request.
If it can't handle the request, it forwards it to the next handler in the
chain.
Building the Chain
int main()
{
srand(time(0));
Handler1
root;
Handler2
two;
Handler3
thr;
root.add(&two);
root.add(&thr);
thr.setNext(&root);
for (int
i = 1; i < 10; i++)
{
root.handle(i);
cout
<< '\n';
}
}
In this setup, we link the handlers into a chain. The first handler processes INFO-level logs, followed by WARNING and ERROR handlers. When a request is passed to the chain, each handler evaluates whether it can handle the request or pass it to the next one.
Benefits of Chain of Responsibility in C++
- Flexibility: You can modify the sequence of handlers
easily by changing their order.
- Loose Coupling: The sender of the request doesn’t need to
know which handler will process it, making your code more maintainable.
- Open/Closed Principle: You can extend the chain by adding new handlers without modifying existing ones.
Enhancing Flexibility
The Chain of Responsibility pattern allows you to add more handlers over time without modifying existing code, following SOLID principles.
This pattern is also widely used in scenarios such as:
- Event handling systems: Where multiple listeners may need
to handle an event.
- Middleware in web development: Processing requests in
stages.
- Command processing: Where multiple commands can be issued and handled by different receivers.
For more in-depth information about design patterns, you can visit this Gang of Four DesignPatterns.
Conclusion
The Chain of Responsibility Design Pattern provides a flexible and decoupled way to handle requests in your applications. By allowing requests to pass through a chain of handlers, this pattern makes your code cleaner, more flexible, and easier to maintain. In C++ programming, it's a highly effective pattern when you need to delegate responsibilities across various objects, especially in complex systems.
If you're interested in exploring more about C++ design patterns, be sure to check out other patterns like the Strategy Pattern or the Decorator Pattern, as they also complement this architecture.
Comments
Post a Comment