Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions docs/uml/patterns_behavioral_command.drawio.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions src/patterns/behavioral/ChainOfCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ namespace
{
namespace CoR
{
/*
* Handler - defines an interface for handling requests
*/
class IHandler
{
public:
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -155,6 +165,7 @@ struct CoRAutoRunner
{
CoRAutoRunner()
{
std::cout << "\n--- CoR Pattern Example ---\n";
CoR::run();
}
};
Expand Down
162 changes: 162 additions & 0 deletions src/patterns/behavioral/Command.cpp
Original file line number Diff line number Diff line change
@@ -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 <iostream>
#include <string>

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;