The Command pattern. Implementation in C++
Contents
- 1. General information. Purpose. Pattern structure
- 2. The ways of use
- 3. Implementation of the Command pattern (based on the structure drawing) in C++. Simplified version (without saving state)
- 4. Relationships
- 5. Results
- 6. Using a template for simple commands without arguments. Example in C++
- Related topics
1. General information. Purpose. Pattern structure
The Command pattern refers to patterns of object behavior. The pattern also has other names: Action, Transaction.
The peculiarity of the pattern is that a request to execute a command is represented as an object, which is passed to this command as a parameter and processed accordingly. Also, the request can be queued and support cancellation of operations (the state of the command is saved).
The structure of the Command pattern is shown in Figure 1.
Figure 1. The structure of Command pattern
The participants of the Command pattern are the following.
- Command – interface (abstract class) designed to perform a specific method (operation).
- ConcreteCommand – a specific command that:
- defines the relationship between the Receiver object and the action;
- implements the Execute() method (operation), which calls the corresponding method of the Receiver object.
- Client – a client (client class) that creates an object of the ConcreteCommand class and sets its recipient.
- Invoker – the initiator of the request, who contacts the team to execute the request.
- Receiver – a recipient class containing methods (operations) that satisfy the request. The recipient can be any class.
⇑
2. The ways of use
The Command pattern is used in the following cases.
- When it is necessary to parameterize objects with a given action.
- If the created request needs to be queued and executed at different times. This allows the command to be executed in another process.
- When it is necessary to support the cancellation of operations. Here, state is saved to roll back actions performed by the command.
- If necessary, provide a record of changes that can be repeated after a system emergency stop. The protocol can also be stored in external memory.
- When it is necessary to structure a system from primitive operations combined into higher-level operations.
⇑
3. Implementation of the Command pattern (based on the structure drawing) in C++. Simplified version (without saving state)
// The Command pattern #include <iostream> using namespace std; // Receiver class class Receiver { public: void Action() { cout << "Receiver::Action()" << endl; } }; class Command abstract { public: // Operation virtual void Execute() = 0; }; // In this case, this class is not required; // it is replaced by the template class SimpleCommand<Receiver> class ConcreteCommand : public Command { private: Receiver* receiver; public: // Constructor ConcreteCommand(Receiver* _receiver) { receiver = _receiver; } // Implementation of Command interface void Execute() override { receiver->Action(); } }; // Client class - case without template class class Client { private: Receiver* receiver; // reference to Receiver public: // Constructor - gets Receiver Client(Receiver* receiver) { // Create someone who calls this->receiver = receiver; } // Caller method - Invoker void InvokeCommand() { Command* invoker; invoker = new ConcreteCommand(receiver); invoker->Execute(); if (invoker) delete invoker; } }; // Template class. Here you can substitute any other type (class) // instead of the Receiver type. template <class Receiver> class SimpleCommand : public Command { public: typedef void (Receiver::* Action)(); SimpleCommand(Receiver* r, Action a) : _receiver(r), _action(a) { } virtual void Execute() { (_receiver->*_action)(); } private: Action _action; Receiver* _receiver; }; void main() { // --------- Way #1 - with a template class // Client Receiver* receiver = new Receiver; // This is the one who calls (Invoker), // this code can be placed in a method of some class. Command* aCommand = new SimpleCommand<Receiver>(receiver, &Receiver::Action); aCommand->Execute(); // Receiver->Action() if (aCommand != nullptr) delete aCommand; if (receiver != nullptr) delete receiver; // ------------------------------- // ----- Way #2 – without template class receiver = new Receiver; // This is a client class Client client(receiver); client.InvokeCommand(); if (receiver) delete receiver; }
⇑
4. Relationships
The following relationships are distinguished between the participants of the pattern.
- The client creates a concrete command (ConcreteCommand) as an object and sets a recipient for it (Receiver).
- The Invoker saves the ConcreteCommand object.
- The initiator sends the request by calling Execute(). If undoing of completed actions is supported, state information is saved in the ConcreteCommand class before calling Execute(). This information must be such that it is possible to roll back completed actions.
- The ConcreteCommand object calls the Receiver methods to complete the request.
⇑
5. Results
For the Command pattern, the following results can be distinguished.
- The connection is broken between the object that initiated the method (operation) call and the object that has information about how to execute this method.
- Each command is an object that can be manipulated or extended.
- Composite commands can be formed from simple commands, which can be described by the Composite pattern.
- New commands are easy to add because there is no need to modify existing classes. Each new command only adds a new class.
⇑
6. Using a template for simple commands without arguments. Example in C++
If you need to implement simple commands that do not require arguments, you can create a template class whose type will be Receiver.
Below is an example of using such a template class. A template class SimpleCommand<Receiver> is created that defines a simple command.
// The Command pattern #include <iostream> using namespace std; // Class - receiver class Receiver { public: void Action() { cout << "Receiver::Action()" << endl; } }; class Command abstract { public: // Operation virtual void Execute() = 0; }; // In this case, this class is not required, // it is replaced by the template class SimpleCommand<Receiver> class ConcreteCommand : public Command { private: Receiver* receiver; public: // Constructor ConcreteCommand(Receiver* _receiver) { receiver = _receiver; } // Implementation of Command interface void Execute() override { receiver->Action(); } }; // Template class. Here instead of the Receiver type // you can substitute any other type (class). template <class Receiver> class SimpleCommand : public Command { public: typedef void (Receiver::* Action)(); SimpleCommand(Receiver* r, Action a) : _receiver(r), _action(a) { } virtual void Execute() { (_receiver->*_action)(); } private: Action _action; Receiver* _receiver; }; void main() { // Client Receiver* receiver = new Receiver; // This is the one who calls (Invoker), // this code can be placed in a method of some class. Command* aCommand = new SimpleCommand<Receiver>(receiver, &Receiver::Action); aCommand->Execute(); // Receiver->Action() if (aCommand != nullptr) delete aCommand; if (receiver != nullptr) delete receiver; }
⇑
Related topics
- Behavior patterns. Overview
- The Chain of Responsibility pattern. General information. Implementation in C++
- Command. Implementation in C++
- State pattern. Structure. Implementation in C++
⇑