//+------------------------------------------------------------------+
//| Command.mqh |
//| 2019-2020, dimitri pecheritsa |
//| 792112@gmail.com |
//+------------------------------------------------------------------+
//| command — behavioral design pattern |
//+------------------------------------------------------------------+
// design patterns: elements of reusable object-oriented software
// gof > erich gamma, richard helm, ralph johnson, john vlissides
// published — 1994
//+------------------------------------------------------------------+
//| intent |
//+------------------------------------------------------------------+
// encapsulate a request as an object
// thereby letting you parameterize clients with different requests
// queue or log requests, and support undoable operations
//+------------------------------------------------------------------+
//| benefits |
//+------------------------------------------------------------------+
// variable aspect > when and how a request is fulfilled
// refactoring
// problems
// dependence on specific operations
// tight coupling
// solutions
// avoid hard-coded requests > also > chain of responsibility
// decouple with abstract coupling and layering
// also > abstract factory, bridge, chain of responsibility,
// facade, mediator, observer
//+------------------------------------------------------------------+
//| applicability |
//+------------------------------------------------------------------+
// parameterize objects by an action to perform
// specify, queue, and execute requests at different times
// command is independent of the original request
// support undo
// support change log
// reapply after a system crash
// add load/store to command
// reload from disk
// reexecute with the execute
// structure a system around high-level operations
// built on primitives operations
//+------------------------------------------------------------------+
//| structure |
//+------------------------------------------------------------------+
//
// |Client| |Invoker|o------------------->| Command |
// | | |---------|
// | |Execute()|
// | +------->|Receiver| ^
// |--------| receiver |
// | |Action()|<--------------| ConcreteCommand |
// |------------------|
// | |Execute() |
// | receiver.Action()|
// | |------------------|
// + - - - - - - - - - - - - - - - - - >|state |
//
#include <SRC\Patterns\PatternOrganizer.mqh>
namespace Command
{
//+------------------------------------------------------------------+
//| participants > receiver |
//+------------------------------------------------------------------+
class Receiver
// knows how to perform the request operations
// any class may be a receiver
{
public:
Receiver(void);
Receiver(Receiver&);
void Action(void);
};
//+------------------------------------------------------------------+
//| participants > receiver > default |
//+------------------------------------------------------------------+
Receiver::Receiver(void)
{
}
//+------------------------------------------------------------------+
//| participants > receiver > copy constructor |
//+------------------------------------------------------------------+
Receiver::Receiver(Receiver &src)
{
}
//+------------------------------------------------------------------+
//| participants > receiver > action |
//+------------------------------------------------------------------+
void Receiver::Action(void)
{
Print("receiver ",&this," action");
}
//+------------------------------------------------------------------+
//| participants > command |
//+------------------------------------------------------------------+
class Command
// declares operation interface
{
protected:
Receiver* m_receiver;
public:
Command(Receiver*);
~Command(void);
virtual void Execute(void)=0;
};
//+------------------------------------------------------------------+
//| participants > command > constructor |
//+------------------------------------------------------------------+
Command::Command(Receiver* receiver)
{
m_receiver=new Receiver(receiver);
Print("receiver ",receiver," accepted by command ",&this);
}
//+------------------------------------------------------------------+
//| participants > command > destructor |
//+------------------------------------------------------------------+
Command::~Command(void)
{
if(CheckPointer(m_receiver)==1)
{
delete m_receiver;
}
}
//+------------------------------------------------------------------+
//| participants > concrete command |
//+------------------------------------------------------------------+
class ConcreteCommand:public Command
// receiver/action binding
// implements execute() by calling receiver operation
{
protected:
int m_state;
public:
ConcreteCommand(Receiver*);
void Execute(void);
};
//+------------------------------------------------------------------+
//| participants > concrete command > constructor |
//+------------------------------------------------------------------+
ConcreteCommand::ConcreteCommand(Receiver* receiver):
Command(receiver),
m_state(0)
{
Print("command ",&this," state: ",m_state);
}
//+------------------------------------------------------------------+
//| participants > concrete command > execute |
//+------------------------------------------------------------------+
void ConcreteCommand::Execute(void)
{
Print("command executes receiver ",m_receiver);
m_receiver.Action();
m_state=1;
Print("command ",&this," state: ",m_state);
}
//+------------------------------------------------------------------+
//| participants > invoker |
//+------------------------------------------------------------------+
class Invoker
// asks the command to carry out the request
{
public:
~Invoker(void);
void StoreCommand(Command*);
void Execute(void);
protected:
Command* m_command;
};
//+------------------------------------------------------------------+
//| participants > invoker > destructor |
//+------------------------------------------------------------------+
Invoker::~Invoker(void)
{
if(CheckPointer(m_command)==1)
{
delete m_command;
}
}
//+------------------------------------------------------------------+
//| participants > invoker > command |
//+------------------------------------------------------------------+
void Invoker::StoreCommand(Command* command)
{
m_command=command;
Print("command ",m_command," stored");
}
//+------------------------------------------------------------------+
//| participants > invoker > execute |
//+------------------------------------------------------------------+
void Invoker::Execute(void)
{
Print("executing command ",m_command);
m_command.Execute();
}
//+------------------------------------------------------------------+
//| participants > client |
//+------------------------------------------------------------------+
class Client:public ClientExample
// creates a concrete command object and sets its receiver
{
public:
string Output(void);
void Run(void);
};
string Client::Output(void)
{
return __FUNCTION__;
}
//+------------------------------------------------------------------+
//| collaborations |
//+------------------------------------------------------------------+
void Client::Run(void)
// client
// creates a concrete command
// specifies its receiver
// invoker
// stores the concrete command requests by calling execute()
// command undoable
// concrete command stores state prior to execute()
//
// aReceiver aClient aCommand anInvoker
// | | | |
// | | |
// | | |newCommand(aReceiver) | |
// | | |- - - - - - - - - - - ->| | |
// | | | | |
// | | |StoreCommand(aCommand) | |
// | | |-------------------------|------------->| |
// | | | | |
// | | | |
// / / / /
// / / / /
// | | | |
// | | | Execute()| |
// | | Action()| |<------------| |
// | |<--------|-------------------------| | | |
// | | | |
{
Receiver receiver;
Invoker invoker;
invoker.StoreCommand(new ConcreteCommand(&receiver));
invoker.Execute();
}
}
//+------------------------------------------------------------------+
//| output |
//+------------------------------------------------------------------+
// Command::Client::Output
// receiver 2097152 accepted by command 4194304
// command 4194304 state: 0
// command 4194304 stored
// executing command 4194304
// command executes receiver 5242880
// receiver 5242880 action
// command 4194304 state: 1
//+------------------------------------------------------------------+
//| consequences |
//+------------------------------------------------------------------+
// decouple operation's caller/performer
// commands are first-class manipulateable/extendable objects
// assemble commands into a composite command
// easy to add new commands
//+------------------------------------------------------------------+
//| implementation |
//+------------------------------------------------------------------+
// how intelligent is a command
// binds receiver/actions
// delegates nothing to receiver
// commands are independent of the existing classes
// no receiver exists
// command knows receiver
// commands can find receiver dynamically
// undo and redo
// concrete command stores additional state
// receiver
// operation arguments
// receiver's original values that can change receiver
// provides the recover operations
// multiple-level
// history list of sequences of commands
// reverse-execute to cancel
// maybe copy an undoable command before putting it on list
// error accumulation in undo
// delay can be a problem
// errors can accumulate
// use memento to store more original state in the command
// for recover
// templates for non-undoable/no argument commands
//+------------------------------------------------------------------+
//| related patterns |
//+------------------------------------------------------------------+
// composite > implement macrocommands
// memento > keep state the command requires to undo its effect
// prototype > a command that must be copied
// before being placed on the history list
//+------------------------------------------------------------------+
#31361LibrariesMetaTrader 5👁 3819 views
Command - behavioral design pattern
D
Author
DMITRII PECHERITSAPublished date
Oct 7, 2020
📚Related Articles
← View other articles →#77531Libraries
CSetupLedger: a per-setup win rate ledger for Expert Advisors
Why I wrote it My scalper trades two setups. A bullish order block and the bearish mirror of it. The Strategy Tester gave me one profit factor for both of them. I spent two weeks t...
Ali AkbarView detail →
#77488Libraries
Position sizing from risk, using the broker's own tick value and volume step
Every lot size calculator that assumes one pip is worth ten dollars is wrong on gold, wrong on indices, wrong on JPY crosses and wrong on any account not denominated in USD which i...
Ralph Ivan SimeonView detail →
#77156Libraries
PropFirm Defense
A prop firm account runs under a second rulebook MetaTrader 5 knows nothing about: a maximum loss measured from a fixed starting balance, a daily limit that resets at an hour the b...
Bruno Nunes Myrrha RibeiroView detail →