📜 ⬆️ ⬇️

Chain of Responsibility Design Pattern

Read the description of other patterns.


Problem


Efficiently and compactly implement the mechanism for processing the flow of events / requests / messages in systems with potentially large number of processors.

Description


The event / handler model is widely used in software systems from various fields. Basically, this is a graphical user interface, where events generated from user actions are handled in various ways by interface elements. It is impossible to forget about WinAPI, which very often implements such a model. In most sources, this model is called the Event Loop .
')

On the one hand, everything is quite transparent. There are many types of messages, there are many processors of these messages. The task is to process each message in the stream associated with its specific type of handler. Simply put, you need to have an effective mechanism for linking message types and handlers.

In most cases, with a fixed and a small number of handlers in the system, it is sufficient to implement this mechanism based on the if-then-else construct. However, it is not always that simple. First, it is not always known in advance how many processors are needed. Secondly, the addition of a new type of message to the system usually leads to the emergence of a new handler, the implementation of which already becomes a really difficult task.

To solve such problems, there is a pattern - “Chain of Responsibilities”.

The template idea is to organize a recurrent pipeline from handlers, in which each processor can either process the incoming message (for example, only messages of a certain type) or delegate processing to the next processor in the pipeline. There is another option for processing and subsequent transfer. At the same time, in order to initiate the processing of a message, it is enough to pass it on to the first processor in the pipeline.

Practical example


Consider, perhaps the most obvious example of a template - a computer network. Indeed - a large number of handlers - network nodes (computers, servers, routers) and even a large number of types of network requests.

Suppose that in a simplified and invented network model there are 4 types of handlers - a network, a router, a forwarder and a server. So there is only one type of request - a request for processing by the server. Handlers have the following behavior: the network simply sends a request in its environment, the router sends the request from one network to another, the forwarder sends the request to a specific host, the server processes the request.

Network nodes are a pipeline of handlers. Request - specific message. The request, moving along the chain is processed (routed, forwards) by each node and passed on. Until it is finally processed by the server.

Class diagram


The main point to which attention should be paid is the method of organizing pipeline processing. In this case, the following approach is used. All handlers implement one abstract class, RequestHandled, which contains a reference to itself (successor) to delegate processing responsibilities to the next handler in the pipeline. The default implementation of the handleRequest () method implements such a delegation.


C ++ implementation


//  #ifndef REQUEST_H #define REQUEST_H #include <string> using namespace std; class Request { private: protected: public: string requestString; Request(string requestString) : requestString(requestString) { } }; #endif //  #ifndef REQUEST_HANDLER_H #define REQUEST_HANDLER_H #include "Request.h" class RequestHandler { protected: RequestHandler *successor; public: RequestHandler(); RequestHandler(RequestHandler *successor) : successor(successor) { } virtual void handleRequest(const Request& request) { successor->handleRequest(request); } }; RequestHandler::RequestHandler() { } #endif //  #ifndef HOST_H #define HOST_H #include "RequestHandler.h" class Host : public RequestHandler { private: protected: public: Host(); Host(Host *host); }; Host::Host() { } Host::Host(Host *host) : RequestHandler((RequestHandler*)host) { } #endif //  #ifndef NETWORK_H #define NETWORK_H #include "RequestHandler.h" #include "Router.h" class Network : public RequestHandler { private: protected: public: Network(Host *host); }; Network::Network(Host *host) : RequestHandler((RequestHandler*)host) { } #endif //  #ifndef ROUTER_H #define ROUTER_H #include "Host.h" #include "Network.h" #include "RequestHandler.h" #include "Request.h" #include <cstdlib> #include <iostream> using namespace std; class Router : public Host { private: void route(Network *network, const Request& request) { if (network != NULL) { ((RequestHandler*)network)->handleRequest(request); } else { cout << "ER: Network is unreachable. Request with string " << request.requestString << " was lost" << endl; } } protected: public: Router(Network *network); virtual void handleRequest(const Request& request) { route((Network*) successor, request); } }; Router::Router(Network *network) { successor = (RequestHandler*) network; } #endif //  #ifndef SERVER_H #define SERVER_H #include "Host.h" #include "Request.h" #include "string" using namespace std; class Server : public Host { private: void showMessage(const string& msg) { cout << msg << endl; } protected: public: Server() : Host(NULL) { } virtual ~Server(); virtual void handleRequest(const Request& request) { string messageStr = "Request received with string: " + request.requestString; showMessage(messageStr); } }; #endif //  #ifndef FORWARDER_H #define FORWARDER_H #include "Host.h" #include "Request.h" class Forwarder : public Host { private: protected: public: Forwarder(Host *host) : Host(host) { } virtual ~Forwarder(); }; #endif //  #include <iostream> #include <cstdlib> using namespace std; #include "Server.h" #include "Router.h" #include "Forwarder.h" #include "Network.h" int main(int argc, char *argv[]) { Server *webServer = new Server(); Forwarder *fw1 = new Forwarder(webServer); Forwarder *fw2 = new Forwarder(fw1); Network *network = new Network(fw2); Router *router = new Router(network); const Request *req = new Request("correct request"); router->handleRequest(*req); Router *router1 = new Router(NULL); Forwarder *fw3 = new Forwarder(router1); Forwarder *fw4 = new Forwarder(fw3); Network *network2 = new Network(fw4); Router *router2 = new Router(network2); const Request *req1 = new Request("incorrect request"); router2->handleRequest(*req1); system("pause"); } 


PS


An attentive reader will say - “Oh! I already read this guy's articles! ”. That's right, I decided to continue the series of articles on design patterns and look forward to your feedback.

Source: https://habr.com/ru/post/113995/


All Articles