diff --git a/CMakeLists.txt b/CMakeLists.txt index 222f90c..8e974cb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -99,6 +99,7 @@ set(APP_SOURCES "src/patterns/structural/Facade.cpp" "src/patterns/structural/Decorator.cpp" "src/patterns/behavioral/ChainOfCommand.cpp" + "src/patterns/behavioral/Command.cpp" ) # Test files diff --git a/docs/uml/patterns_behavioral_command.drawio.svg b/docs/uml/patterns_behavioral_command.drawio.svg new file mode 100644 index 0000000..26151c5 --- /dev/null +++ b/docs/uml/patterns_behavioral_command.drawio.svg @@ -0,0 +1,4 @@ + + + +
Use
<<instantiate>>

<<Interface>>
ICommand


+ execute(Type): Type

SimpleConcreteCommand
+ execute(Type): Type
ComplexConcreteCommand
- m_receiver: Receiver*
- m_field: type
+ execute(Type): Type
Receiver
+ field: type
+ doOperation(type): type
Client
+ clientCode(Invoker* involker): type
Use
clientCode:
{
invoker->invoke
}
Invoker
- m_command1: ICommand*
- m_command2: ICommand*
+ setCommand1(ICommand* cmd)
+ setCommand2(ICommand* cmd)
+ invoke(type) :  type
<<instantiate>>
1
1
invoke:
{
 if(m_command1 != nullptr){
this->m_command1->execute();
 }
...
}
execute:
{
 this->m_receiver->doOperation(m_field);
}
\ No newline at end of file diff --git a/src/patterns/behavioral/ChainOfCommand.cpp b/src/patterns/behavioral/ChainOfCommand.cpp index 7331adc..5d52c1f 100644 --- a/src/patterns/behavioral/ChainOfCommand.cpp +++ b/src/patterns/behavioral/ChainOfCommand.cpp @@ -16,6 +16,9 @@ namespace { namespace CoR { + /* + * Handler - defines an interface for handling requests + */ class IHandler { public: @@ -57,6 +60,10 @@ namespace } }; + /** + * CoreteHandlers - handles the requests it is responsible for + * If it can handle the request it does so, otherwise it sends the request to its successor + */ class ConcreteHandlerGET : public AbstractHandler { private: @@ -116,6 +123,9 @@ namespace } }; + /** + * Client - sends commands to the first object in the chain that may handle the command + */ namespace Client { void clientCode(IHandler &handler, const std::string &request) @@ -155,6 +165,7 @@ struct CoRAutoRunner { CoRAutoRunner() { + std::cout << "\n--- CoR Pattern Example ---\n"; CoR::run(); } }; diff --git a/src/patterns/behavioral/Command.cpp b/src/patterns/behavioral/Command.cpp new file mode 100644 index 0000000..b2bbe22 --- /dev/null +++ b/src/patterns/behavioral/Command.cpp @@ -0,0 +1,162 @@ +// Command is a behavioral design pattern that turns a request into a stand-alone object that contains all information about the request. (receivers, payloads) +// This transformation lets you pass requests as a method arguments, delay or queue a request’s execution, and support undoable operations. +// Allows an object to send a command without knowing what object will receive and handle it. +// Appicability: +// (*) when you want to parameterize objects with operations. +// (**) when you want to queue operations, schedule their execution, or execute them remotely. +// (***) when you want to implement reversible operations. + +// UML: docs/uml/patterns_behavioral_command.drawio.svg + +#include +#include + +namespace +{ + namespace Command + { + + /** + * The Command interface usually declares just a single method for executing the command. + * e.g. Save, Undo, Jump, Backup, CreateOrder + */ + class ICommand + { + public: + virtual void execute() const = 0; + }; + + /** + * The Receiver class contains some business logic. Almost any object may act as a receiver. + * Most commands only handle the details of how a request is passed to the receiver, while the receiver itself does the actual work. + * e.g. Document, GameCharacter, DB service + */ + class Receiver + { + public: + static void doCheck() + { + std::cout << "Receiver checking... \n"; + }; + static void doInit() + { + std::cout << "Receiver initializing... \n"; + }; + static void doLaunch(const std::string &arg) + { + std::cout << "Receiver launching... \n\t" << arg << "\n"; + }; + }; + + /** + * Concrete Commands implement various kinds of requests. + * A concrete command isn’t supposed to perform the work on its own, but rather to pass the call to one of the business logic objects. + * However, for the sake of simplifying the code, these classes can be merged. + */ + class SimpleConcreteCommand : public ICommand + { + public: + void execute() const override + { + std::cout << "\t SimpleCommand executed \n"; + } + }; + + class ComplexConcreteCommand : public ICommand + { + private: + Receiver *m_receiver; + std::string m_payload; + + public: + ComplexConcreteCommand(Receiver *receiver, const std::string &payload) : m_receiver{receiver}, m_payload{payload} {}; + + void execute() const override + { + std::cout << "\t ComplexCommand executed \n"; + this->m_receiver->doCheck(); + this->m_receiver->doInit(); + this->m_receiver->doLaunch(m_payload); + } + }; + + /** + *The Sender class (aka invoker) is responsible for initiating requests. + *This class must have a field for storing a reference to a command object. + *The sender triggers that command instead of sending the request directly to the receiver. + *Note that the sender isn’t responsible for creating the command object. Usually, it gets a pre-created command from the client via the constructor. + *e.g. Button, Shortcut, Scheduler, Event bus... + */ + class Invoker + { + private: + ICommand *m_on_start; + ICommand *m_on_finish; + + public: + explicit Invoker(ICommand *s = nullptr, ICommand *f = nullptr) : m_on_start{s}, m_on_finish{s} + { + } + + ~Invoker() + { + delete m_on_start; + delete m_on_finish; + } + + void setOnStart(ICommand *command) + { + this->m_on_start = command; + } + + void setOnFinish(ICommand *command) + { + this->m_on_finish = command; + } + + void invoke() const + { + if (m_on_start != nullptr) + { + m_on_start->execute(); + } + + if (m_on_finish != nullptr) + { + m_on_finish->execute(); + } + } + }; + + namespace Client + { + void clientCode(const Invoker *invoker) + { + invoker->invoke(); + } + + } + + void run() + { + Receiver *ui = new Receiver(); + // How to execute these command when something triggered + Invoker *invoker = new Invoker(); + invoker->setOnStart(new SimpleConcreteCommand()); + invoker->setOnFinish(new ComplexConcreteCommand(ui, "cmd --version")); + Client::clientCode(invoker); + delete ui; + } + } +} + +struct CommandAutoRunner +{ + CommandAutoRunner() + { + std::cout << "\n--- Command Pattern Example ---\n"; + Command::run(); + } +}; + +static CommandAutoRunner instance; \ No newline at end of file