Patterns. The Command pattern. Implementation in C++

The Command pattern. Implementation in C++

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.

Pattern Command. Structure

Figure 1. The structure of Command pattern

Участниками паттерна Command являются следующие.

  1. Command – interface (abstract class) designed to perform a specific method (operation).
  2. 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.
  1. Client – a client (client class) that creates an object of the ConcreteCommand class and sets its recipient.
  2. Invoker – the initiator of the request, who contacts the team to execute the request.
  3. 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.

  1. When it is necessary to parameterize objects with a given action.
  2. If the created request needs to be queued and executed at different times. This allows the command to be executed in another process.
  3. When it is necessary to support the cancellation of operations. Here, state is saved to roll back actions performed by the command.
  4. 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.
  5. 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.

  1. The client creates a concrete command (ConcreteCommand) as an object and sets a recipient for it (Receiver).
  2. The Invoker saves the ConcreteCommand object.
  3. 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.
  4. The ConcreteCommand object calls the Receiver methods to complete the request.

 

5. Results

For the Command pattern, the following results can be distinguished.

  1. 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.
  2. Each command is an object that can be manipulated or extended.
  3. Composite commands can be formed from simple commands, which can be described by the Composite pattern.
  4. 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;   
}