The Command design pattern creates a list of objects on which we're going to apply a generic method.
It's a bit unusal because an object will request a method as parameter.
This makes the Command design pattern a versatile one but maybe not so easy to handle at first.
Once again an interesting study case.
Let's see that in this Command design pattern tutorial.
First of all
There are many ways to use this Command design pattern.
It clearly depends on what you need in your program.
For example you can have Command classes already existing in your code so you'll have to add only an interface to them.
You could also have only one receiver that will handle every action, so don't need to specifiy it each time on the command (even if it's always better to think to the future and anticipated the fact that other receivers will be necessary).
Or maybe you want a generic Command that will take a receiver and an action as parameters but you don't want an interface for the receivers, a template will be a solution.
So according to your needs the end will be different.
In general, this design pattern needs a function pointer as a callback.
The declaration of a function pointer is a bit esoteric so don't be afraid of that.
With this function pointer we will tell the Command which method apply on the object passed as parameter.
The object and the method will be both used as a pair by the Command.
So we need them, they are essential in this design pattern.
It' also possible to not use any method as second parameter for the Command but in this case we'd assume that there would be only one method in the Receiver.
Anyway the key to this pattern is the execute() method that will be used every time the Invoker launch a command.
Lets' code a bit.
Code
CommandFly.cpp
#include "CommandFly.h"
#include <iostream>
// Badprog.com
//-----------------------------------------------------------------------------
// CTOR
//-----------------------------------------------------------------------------
CommandFly::CommandFly() {
std::cout << __FUNCTION__ << std::endl;
}
//-----------------------------------------------------------------------------
// CTOR parameterized
//-----------------------------------------------------------------------------
CommandFly::CommandFly(std::unique_ptr<MainApplication> application)
: _application(std::move(application)) {
std::cout << __FUNCTION__ << std::endl;
}
//-----------------------------------------------------------------------------
// DTOR
//-----------------------------------------------------------------------------
CommandFly::~CommandFly() {
std::cout << __FUNCTION__ << std::endl;
}
//-----------------------------------------------------------------------------
// execute
//-----------------------------------------------------------------------------
void CommandFly::execute() {
std::cout << __FUNCTION__ << std::endl;
_application->goodEvening();
}
CommandFly.h
#ifndef __BADPROG_COMMAND_FLY_H__
#define __BADPROG_COMMAND_FLY_H__
// Badprog.com
#include <memory>
#include "ICommand.h"
#include "MainApplication.h"
class CommandFly : public ICommand
{
public:
CommandFly();
CommandFly(std::unique_ptr<MainApplication> application); // needs a pointer because we need an IReceiver (interface)
~CommandFly();
void execute() override;
private:
std::unique_ptr<MainApplication> _application;
};
#endif // __BADPROG_COMMAND_FLY_H__
CommandRocket.cpp
#include "CommandRocket.h"
#include <iostream>
// Badprog.com
//-----------------------------------------------------------------------------
// CTOR
//-----------------------------------------------------------------------------
CommandRocket::CommandRocket() {
std::cout << __FUNCTION__ << std::endl;
}
//-----------------------------------------------------------------------------
// CTOR
//-----------------------------------------------------------------------------
CommandRocket::CommandRocket(std::unique_ptr<SpecialObject> specialObject)
: _specialObject(std::move(specialObject)) {
std::cout << __FUNCTION__ << std::endl;
}
//-----------------------------------------------------------------------------
// DTOR
//-----------------------------------------------------------------------------
CommandRocket::~CommandRocket() {
std::cout << __FUNCTION__ << std::endl;
}
//-----------------------------------------------------------------------------
// execute
//-----------------------------------------------------------------------------
void CommandRocket::execute() {
std::cout << __FUNCTION__ << std::endl;
_specialObject->goodMorning();
}
CommandRocket.h
#ifndef __BADPROG_COMMAND_ROCKET_H__
#define __BADPROG_COMMAND_ROCKET_H__
// Badprog.com
#include <memory>
#include "ICommand.h"
#include "SpecialObject.h"
class CommandRocket : public ICommand
{
public:
CommandRocket();
CommandRocket(std::unique_ptr<SpecialObject> specialObject);
~CommandRocket();
void execute() override;
private:
std::unique_ptr<SpecialObject> _specialObject;
};
#endif // __BADPROG_COMMAND_ROCKET_H__
main.cpp
//
#include <memory>
#include "Invoker.h"
#include "CommandFly.h"
#include "CommandRocket.h"
#include "ReceiverAlpha.h"
#include "ReceiverBeta.h"
#include "MainApplication.h"
#include "SpecialObject.h"
// Badprog.com
//-----------------------------------------------------------------------------
// main
//-----------------------------------------------------------------------------
int main() {
// init
auto receiverAlpha = std::make_shared<ReceiverAlpha>();
auto receiverBeta = std::make_shared<ReceiverBeta>();
auto mainApplication = std::make_unique<MainApplication>();
auto specialObject = std::make_unique<SpecialObject>();
auto commandFly = std::make_unique<CommandFly>(std::move(mainApplication));
auto commandRocket = std::make_unique<CommandRocket>(std::move(specialObject));
auto commandBasicAlpha1 = std::make_unique<CommandBasic<ReceiverAlpha>>(receiverAlpha, &ReceiverAlpha::greetings);
auto commandBasicAlpha2 = std::make_unique<CommandBasic<ReceiverAlpha>>(receiverAlpha, &ReceiverAlpha::hello);
auto commandBasicAlpha3 = std::make_unique<CommandBasic<ReceiverAlpha>>(receiverAlpha, &ReceiverAlpha::goodbye);
auto commandBasicBeta = std::make_unique<CommandBasic<ReceiverBeta>>(receiverBeta, &ReceiverBeta::howAreYou);
Invoker invoker;
// invoker
invoker.addCommand(std::move(commandBasicAlpha1));
invoker.addCommand(std::move(commandBasicAlpha2));
invoker.addCommand(std::move(commandBasicAlpha3));
invoker.addCommand(std::move(commandBasicBeta));
invoker.addCommand(std::move(commandFly));
invoker.addCommand(std::move(commandRocket));
invoker.executeVectorOfCommands();
return 0;
}
ICommand.h
#ifndef __BADPROG_ICOMMAND_H__
#define __BADPROG_ICOMMAND_H__
#include <iostream>
#include <memory>
// Badprog.com
class ICommand
{
public:
virtual ~ICommand() = 0;
virtual void execute() = 0;
protected:
ICommand& operator=(const ICommand&) { return *this; }
};
inline ICommand::~ICommand() {}
//_____________________________________________________________________________
// Template
//_____________________________________________________________________________
//-----------------------------------------------------------------------------
// CommandBasic
//-----------------------------------------------------------------------------
template <typename T>
class CommandBasic : public ICommand {
public:
typedef void (T:: *Action)(); // Action is a function pointer to T
//-----------------------------------------------------------------------------
// CTOR
//-----------------------------------------------------------------------------
// CommandBasic(T *receiver, Action action) :
CommandBasic(std::shared_ptr<T> receiver, Action action)
: _receiver(receiver), _action(action) {
std::cout << __FUNCTION__ << std::endl;
}
//-----------------------------------------------------------------------------
// DTOR
//-----------------------------------------------------------------------------
~CommandBasic() {
std::cout << __FUNCTION__ << std::endl;
}
// in case we want to copy a CommandBasic outside the template
CommandBasic(const CommandBasic &other) : _receiver(std::make_shared(*other._receiver)) {}
CommandBasic(CommandBasic&& other) : _receiver(std::move(other._receiver)) {}
CommandBasic& operator=(CommandBasic other) {
std::swap(*this, other);
return *this;
}
//-----------------------------------------------------------------------------
// execute
//-----------------------------------------------------------------------------
virtual void execute() {
std::cout << __FUNCTION__ << std::endl;
(_receiver.get()->*_action)();
}
private:
Action _action; // Action is already a pointer
std::shared_ptr<T> _receiver;
};
#endif // __BADPROG_ICOMMAND_H__
Invoker.cpp
#include "Invoker.h"
#include <iostream>
// Badprog.com
//-----------------------------------------------------------------------------
// CTOR
//-----------------------------------------------------------------------------
Invoker::Invoker() {
std::cout << __FUNCTION__ << std::endl;
}
//-----------------------------------------------------------------------------
// DTOR
//-----------------------------------------------------------------------------
Invoker::~Invoker() {
std::cout << __FUNCTION__ << std::endl;
}
//-----------------------------------------------------------------------------
// addCommand
//-----------------------------------------------------------------------------
void Invoker::addCommand(std::unique_ptr<ICommand> command) {
std::cout << __FUNCTION__ << std::endl;
_vectorOfCommands.push_back(std::move(command));
}
//-----------------------------------------------------------------------------
// executeVectorOfCommands
//-----------------------------------------------------------------------------
void Invoker::executeVectorOfCommands() const {
std::cout << std::endl;
std::cout << __FUNCTION__ << std::endl;
for (unsigned int i = 0; i < _vectorOfCommands.size(); ++i) {
_vectorOfCommands[i]->execute();
}
std::cout << std::endl;
}
Invoker.h
#ifndef __BADPROG_INVOKER_H__
#define __BADPROG_INVOKER_H__
// Badprog.com
#include <memory>
#include <vector>
#include "ICommand.h"
class Invoker
{
public:
Invoker();
~Invoker();
void addCommand(std::unique_ptr<ICommand> command);
void executeVectorOfCommands() const;
private:
std::vector<std::unique_ptr<ICommand>> _vectorOfCommands;
};
#endif // __BADPROG_INVOKER_H__
MainApplication.cpp
#include "MainApplication.h"
#include <iostream>
// Badprog.com
//-----------------------------------------------------------------------------
// CTOR
//-----------------------------------------------------------------------------
MainApplication::MainApplication() {
std::cout << __FUNCTION__ << std::endl;
}
//-----------------------------------------------------------------------------
// DTOR
//-----------------------------------------------------------------------------
MainApplication::~MainApplication() {
std::cout << __FUNCTION__ << std::endl;
}
//-----------------------------------------------------------------------------
// goodEvening
//-----------------------------------------------------------------------------
void MainApplication::goodEvening() const {
std::cout << __FUNCTION__ << std::endl;
}
MainApplication.h
#ifndef __BADPROG_MAIN_APPLICATION_H__
#define __BADPROG_MAIN_APPLICATION_H__
#include <iostream>
// Badprog.com
class MainApplication
{
public:
MainApplication();
~MainApplication();
void goodEvening() const;
};
#endif // __BADPROG_MAIN_APPLICATION_H__
ReceiverAlpha.cpp
#include "ReceiverAlpha.h"
#include <iostream>
// Badprog.com
//-----------------------------------------------------------------------------
// CTOR
//-----------------------------------------------------------------------------
ReceiverAlpha::ReceiverAlpha() : _greetings("Have a nice day :p") {
std::cout << __FUNCTION__ << std::endl;
}
//-----------------------------------------------------------------------------
// DTOR
//-----------------------------------------------------------------------------
ReceiverAlpha::~ReceiverAlpha() {
std::cout << __FUNCTION__ << std::endl;
}
//-----------------------------------------------------------------------------
// hello
//-----------------------------------------------------------------------------
void ReceiverAlpha::hello() {
std::cout << __FUNCTION__ << std::endl;
_greetings = "Hello world! :D";
std::cout << _greetings << std::endl;
}
//-----------------------------------------------------------------------------
// greetings
//-----------------------------------------------------------------------------
void ReceiverAlpha::greetings() {
std::cout << __FUNCTION__ << std::endl;
std::cout << _greetings << std::endl;
}
//-----------------------------------------------------------------------------
// goodbye
//-----------------------------------------------------------------------------
void ReceiverAlpha::goodbye() {
std::cout << __FUNCTION__ << std::endl;
_greetings = "Goodbye see you soon ;)";
std::cout << _greetings << std::endl;
}
ReceiverAlpha.h
#ifndef __BADPROG_RECEIVER_ALPHA_H__
#define __BADPROG_RECEIVER_ALPHA_H__
// Badprog.com
#include <string>
class ReceiverAlpha
{
public:
ReceiverAlpha();
~ReceiverAlpha();
void hello();
void goodbye();
void greetings();
private:
std::string _greetings;
};
#endif // __BADPROG_RECEIVER_ALPHA_H__
ReceiverBeta.cpp
#include "ReceiverBeta.h"
#include <iostream>
// Badprog.com
//-----------------------------------------------------------------------------
// CTOR
//-----------------------------------------------------------------------------
ReceiverBeta::ReceiverBeta() {
std::cout << __FUNCTION__ << std::endl;
}
//-----------------------------------------------------------------------------
// DTOR
//-----------------------------------------------------------------------------
ReceiverBeta::~ReceiverBeta() {
std::cout << __FUNCTION__ << std::endl;
}
//-----------------------------------------------------------------------------
// howAreYou
//-----------------------------------------------------------------------------
void ReceiverBeta::howAreYou() {
std::cout << __FUNCTION__ << std::endl;
}
ReceiverBeta.h
#ifndef __BADPROG_RECEIVER_BETA_H__
#define __BADPROG_RECEIVER_BETA_H__
// Badprog.com
class ReceiverBeta
{
public:
ReceiverBeta();
~ReceiverBeta();
void howAreYou();
};
#endif // __BADPROG_RECEIVER_BETA_H__
SpecialObject.cpp
#include "SpecialObject.h"
#include <iostream>
// Badprog.com
//-----------------------------------------------------------------------------
// CTOR
//-----------------------------------------------------------------------------
SpecialObject::SpecialObject() {
std::cout << __FUNCTION__ << std::endl;
}
//-----------------------------------------------------------------------------
// DTOR
//-----------------------------------------------------------------------------
SpecialObject::~SpecialObject() {
std::cout << __FUNCTION__ << std::endl;
}
//-----------------------------------------------------------------------------
// goodMorning
//-----------------------------------------------------------------------------
void SpecialObject::goodMorning() const {
std::cout << __FUNCTION__ << std::endl;
}
SpecialObject.h
#ifndef __BADPROG_SPECIAL_OBJECT_H__
#define __BADPROG_SPECIAL_OBJECT_H__
#include <iostream>
// Badprog.com
class SpecialObject
{
public:
SpecialObject();
~SpecialObject();
void goodMorning() const;
};
#endif // __BADPROG_SPECIAL_OBJECT_H__
Compiling, linking and running
g++ *.cpp -o go.exe -std=c++1y ; ./go.exe
Result
ReceiverAlpha::ReceiverAlpha
ReceiverBeta::ReceiverBeta
MainApplication::MainApplication
SpecialObject::SpecialObject
CommandFly::CommandFly
CommandRocket::CommandRocket
CommandBasic<class ReceiverAlpha>::CommandBasic
CommandBasic<class ReceiverAlpha>::CommandBasic
CommandBasic<class ReceiverAlpha>::CommandBasic
CommandBasic<class ReceiverBeta>::CommandBasic
Invoker::Invoker
Invoker::addCommand
Invoker::addCommand
Invoker::addCommand
Invoker::addCommand
Invoker::addCommand
Invoker::addCommand
Invoker::executeVectorOfCommands
CommandBasic<class ReceiverAlpha>::execute
ReceiverAlpha::greetings
Have a nice day :p
CommandBasic<class ReceiverAlpha>::execute
ReceiverAlpha::hello
Hello world! :D
CommandBasic<class ReceiverAlpha>::execute
ReceiverAlpha::goodbye
Goodbye see you soon ;)
CommandBasic<class ReceiverBeta>::execute
ReceiverBeta::howAreYou
CommandFly::execute
MainApplication::goodEvening
CommandRocket::execute
SpecialObject::goodMorning
Invoker::~Invoker
CommandBasic<class ReceiverAlpha>::~CommandBasic
CommandBasic<class ReceiverAlpha>::~CommandBasic
CommandBasic<class ReceiverAlpha>::~CommandBasic
CommandBasic<class ReceiverBeta>::~CommandBasic
CommandFly::~CommandFly
MainApplication::~MainApplication
CommandRocket::~CommandRocket
SpecialObject::~SpecialObject
ReceiverBeta::~ReceiverBeta
ReceiverAlpha::~ReceiverAlpha
Conclusion
An interesting way to handle different kind of Commands.
Good job if you got it!
Comments
james mason (not verified)
Tuesday, July 4, 2023 - 5:09pm
Permalink
This is excellent - struggled
This is excellent - struggled at first but then <light bulb *ON*>
Add new comment