Почитать описание других паттернов.

Эффективно и компактно реализовать механизм обработки потока событий/запросов/сообщений в системах с потенциально большим количеством обработчиков.
Модель событие/обработчик широко применяется в программных системах из различных областей. В основном, это — графический интерфейс пользователя, где события, генерируемые от действий пользователя различным образом обрабатываются элементами интерфейса. Нельзя так-же забывать про WinAPI, который сплошь и рядом реализует такую модель. В большинстве источников эта модель имеет название Event Loop.
С одной стороны — все достаточно прозрачно. Есть множество типов сообщений, есть множество обработчиков этих сообщений. Задача — обрабатывать каждое сообщение в потоке ассоциированным с его конкретными типом обработчиком. Проще говоря, необходимо иметь эффективный механизм увязывания типов сообщений и обработчиков.
В большинстве случаев, с фиксированным и небольшим количеством обработчиков в системе, бывает достаточно реализовать данный механизм на базе конструкции if-then-else. Однако, не всегда все так просто. Во-первых, не всегда заранее известно сколько именно обработчиков необходимо. Во вторых — добавление в систему нового типа сообщения как правило приводит к появлению нового обработчика, внедрение которого уже становится действительно сложной задачей.
Для решения подобных проблем существует шаблон — “Цепочка обязанностей”.
Идея шаблона заключается в организации рекуррентного конвейера из обработчиков, в котором каждый обработчик может либо обработать поступившее сообщение (например, только сообщения определенного типа), либо делегировать обработку следующему в конвейере обработчику. Возможен еще вариант обработки и последующей передачи. При этом, клиенту, чтобы инициировать обработку того или иного сообщения достаточно лишь передать его первому в конвейере обработчику.
Рассмотрим, пожалуй самый наглядный пример шаблона — компьютерную сеть. Действительно — большое количество обработчиков — узлов сети (компьютеров, серверов, маршрутизаторов) и еще большое количество типов сетевых запросов.
Пусть в упрощенной и выдуманной сетевой модели есть 4-типа обработчиков — сеть, маршрутизатор, форвардер и сервер. Так-же есть всего один тип запроса — запрос на обработку сервером. Обработчики обладают следующим поведением: сеть — просто предает по своей среде запрос, маршрутизатор — передает запрос из одной сети в другую, форвардер — передает запрос конкретному хосту, сервер — обрабатывает запрос.
Узлы сети представляют собой конвейер обработчиков. Запрос — конкретное сообщение. Запрос, двигаясь по цепочке обрабатывается (маршрутизируется, форвардится) каждым ее узлом и передается дальше. До тех пор, пока не будет окончательно обработан сервером.
Основной момент на который следует обратить внимание — способ организации конвейерной обработки. В данном случае используется следующий подход. Все обработчики реализуют один абстрактный класс — RequestHandled, который содержит ссылку на самого себя (successor) для делегирования обязанностей по обработке следующему обработчику в конвейере. Реализация метода handleRequest() по-умолчанию реализует такую делегацию.

Внимательный читатель скажет — “О! Я уже читал статьи этого парня!”. Все верно, я решил продолжить серию статей о шаблонах проектирования и надеюсь на ваши фидбеки.

Проблема
Эффективно и компактно реализовать механизм обработки потока событий/запросов/сообщений в системах с потенциально большим количеством обработчиков.
Описание
Модель событие/обработчик широко применяется в программных системах из различных областей. В основном, это — графический интерфейс пользователя, где события, генерируемые от действий пользователя различным образом обрабатываются элементами интерфейса. Нельзя так-же забывать про WinAPI, который сплошь и рядом реализует такую модель. В большинстве источников эта модель имеет название Event Loop.
С одной стороны — все достаточно прозрачно. Есть множество типов сообщений, есть множество обработчиков этих сообщений. Задача — обрабатывать каждое сообщение в потоке ассоциированным с его конкретными типом обработчиком. Проще говоря, необходимо иметь эффективный механизм увязывания типов сообщений и обработчиков.
В большинстве случаев, с фиксированным и небольшим количеством обработчиков в системе, бывает достаточно реализовать данный механизм на базе конструкции if-then-else. Однако, не всегда все так просто. Во-первых, не всегда заранее известно сколько именно обработчиков необходимо. Во вторых — добавление в систему нового типа сообщения как правило приводит к появлению нового обработчика, внедрение которого уже становится действительно сложной задачей.
Для решения подобных проблем существует шаблон — “Цепочка обязанностей”.
Идея шаблона заключается в организации рекуррентного конвейера из обработчиков, в котором каждый обработчик может либо обработать поступившее сообщение (например, только сообщения определенного типа), либо делегировать обработку следующему в конвейере обработчику. Возможен еще вариант обработки и последующей передачи. При этом, клиенту, чтобы инициировать обработку того или иного сообщения достаточно лишь передать его первому в конвейере обработчику.
Практический прим��р
Рассмотрим, пожалуй самый наглядный пример шаблона — компьютерную сеть. Действительно — большое количество обработчиков — узлов сети (компьютеров, серверов, маршрутизаторов) и еще большое количество типов сетевых запросов.
Пусть в упрощенной и выдуманной сетевой модели есть 4-типа обработчиков — сеть, маршрутизатор, форвардер и сервер. Так-же есть всего один тип запроса — запрос на обработку сервером. Обработчики обладают следующим поведением: сеть — просто предает по своей среде запрос, маршрутизатор — передает запрос из одной сети в другую, форвардер — передает запрос конкретному хосту, сервер — обрабатывает запрос.
Узлы сети представляют собой конвейер обработчиков. Запрос — конкретное сообщение. Запрос, двигаясь по цепочке обрабатывается (маршрутизируется, форвардится) каждым ее узлом и передается дальше. До тех пор, пока не будет окончательно обработан сервером.
Диаграмма классов
Основной момент на который следует обратить внимание — способ организации конвейерной обработки. В данном случае используется следующий подход. Все обработчики реализуют один абстрактный класс — RequestHandled, который содержит ссылку на самого себя (successor) для делегирования обязанностей по обработке следующему обработчику в конвейере. Реализация метода handleRequest() по-умолчанию реализует такую делегацию.

Реализация на С++
// Запрос #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
Внимательный читатель скажет — “О! Я уже читал статьи этого парня!”. Все верно, я решил продолжить серию статей о шаблонах проектирования и надеюсь на ваши фидбеки.
