diff --git a/CMakeLists.txt b/CMakeLists.txt index 0048dcc..222f90c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -98,6 +98,7 @@ set(APP_SOURCES "src/patterns/structural/Flyweight.cpp" "src/patterns/structural/Facade.cpp" "src/patterns/structural/Decorator.cpp" + "src/patterns/behavioral/ChainOfCommand.cpp" ) # Test files diff --git a/docs/uml/patterns_behavioral_CoR.drawio.svg b/docs/uml/patterns_behavioral_CoR.drawio.svg new file mode 100644 index 0000000..2650ffa --- /dev/null +++ b/docs/uml/patterns_behavioral_CoR.drawio.svg @@ -0,0 +1,4 @@ + + + +
Client
+ clientCode(IHandler* handler, string request): type

<<Interface>>
IHandler


+ setNextHandler(IHandler* nextHandler): Type
+ handle(string request): Type

AbstractHandler
- m_nextHandler: *IHandler
+ handle(string request): Type
ConcreteHandlerPOST
+ handle(string request): Type
ConcreteHandlerGET
+ handle(string request): Type
ConcreteHandlerPUT
+ handle(string request): Type
Use
Extends
Extends
Extends
1
IHandler* handlerPost = new ConcreteHandlerPOST();
IHandler* handlerGet = new ConcreteHandlerPOST();
IHandler* handlerPost = new ConcreteHandlerPOST();
handlerPost->setNextHandler(handlerGet);
handlerGet->setNextHandler(handlerPost);

clientCode(handlerPost,"request")
handle:
if(canHandle(request)){
// handle it
}else{
// call parent handle to send request to next handler if exist
 AbstractHandler::handle(request)
}
handle:
if(this->m_nextHandler != nullptr){
this->m_nextHandler->handle(request);
}else{
// No handle proccessed request
}
:Client
handler:ConcreteHandlerPOST
handler:ConcreteHandlerGET
handler:ConcreteHandlerPUT
handle
handle
!canhandle
handle
!canhandle
\ No newline at end of file diff --git a/src/patterns/behavioral/ChainOfCommand.cpp b/src/patterns/behavioral/ChainOfCommand.cpp new file mode 100644 index 0000000..7331adc --- /dev/null +++ b/src/patterns/behavioral/ChainOfCommand.cpp @@ -0,0 +1,162 @@ +// CoR is a behavioral design pattern that lets you pass requests along a chain of handlers. +// Upon receiving a request, each handler decides either to process the request +// or to pass it to the next handler in the chain. +// Allows an object to send a command without knowing what object will receive and handle it. +// Appicability: +// (*) when it’s essential to execute several handlers in a particular order. +// (**)when the set of handlers and their order are supposed to change at runtime. + +// UML: docs/uml/patterns_behavioral_CoR.drawio.svg + +#include +#include +#include + +namespace +{ + namespace CoR + { + class IHandler + { + public: + virtual void setNextHandler(IHandler *handler) = 0; + virtual IHandler *setNext(IHandler *handler) = 0; + virtual void handle(const std::string &request) = 0; + }; + + class AbstractHandler : public IHandler + { + private: + IHandler *m_setNext; + + public: + AbstractHandler() : m_setNext{nullptr} {}; + + void setNextHandler(IHandler *handler) override + { + this->m_setNext = handler; + } + + // handler1->setNext(handler2)->setNext(handler3) + IHandler *setNext(IHandler *handler) override + { + this->m_setNext = handler; + return handler; + } + + void handle(const std::string &request) override + { + if (this->m_setNext != nullptr) + { + this->m_setNext->handle(request); + } + else + { + std::cout << "\tNo handler processed request: " << request << "\n"; + } + } + }; + + class ConcreteHandlerGET : public AbstractHandler + { + private: + static constexpr const char *header = "GET"; + + public: + void handle(const std::string &request) override + { + if (request.rfind(header, 0) == 0) + { + // If request is eligible, handle it + std::cout << "\tHandle GET request: " << request << "\n"; + // In realworld, it should be other logics here + } + else + { + AbstractHandler::handle(request); + } + } + }; + + class ConcreteHandlerPUT : public AbstractHandler + { + private: + static constexpr const char *header = "PUT"; + + public: + void handle(const std::string &request) override + { + if (request.rfind(header, 0) == 0) + { + std::cout << "\tHandle PUT request: " << request << "\n"; + } + else + { + AbstractHandler::handle(request); + } + } + }; + + class ConcreteHandlerPOST : public AbstractHandler + { + private: + static constexpr const char *header = "POST"; + + public: + void handle(const std::string &request) override + { + if (request.rfind(header, 0) == 0) + { + std::cout << "\tHandle POST request: " << request << "\n"; + } + else + { + AbstractHandler::handle(request); + } + } + }; + + namespace Client + { + void clientCode(IHandler &handler, const std::string &request) + { + handler.handle(request); + } + } + + void run() + { + // Setup Chain of Responsibility + IHandler *postHandler = new ConcreteHandlerPOST(); + IHandler *gettHandler = new ConcreteHandlerGET(); + IHandler *puttHandler = new ConcreteHandlerPUT(); + postHandler->setNext(gettHandler)->setNext(puttHandler); + + // Send requests to the chain + std::string dummy = "DUMMY .."; + std::string postRequest = "POST /test/demo_form.php HTTP/1.1 .."; + std::string getRequest = "GET /users/123 .."; + std::cout << "Send dummy request\n"; + Client::clientCode(*postHandler, dummy); + std::cout << "Send POST request\n"; + Client::clientCode(*postHandler, postRequest); + std::cout << "Send GET request\n"; + Client::clientCode(*postHandler, getRequest); + + delete postHandler; + delete gettHandler; + delete puttHandler; + } + } + +} + +struct CoRAutoRunner +{ + CoRAutoRunner() + { + CoR::run(); + } +}; + +static CoRAutoRunner instance; \ No newline at end of file