Command Pattern

The Command Pattern is a behavioral design pattern that turns a request or simple operation into a stand-alone object.

This object represents the request, allowing us to parameterize clients with different requests, queue or log requests, and support undoable operations.

The key idea is to decouple the sender (client) from the receiver (executor of the request), promoting loose coupling between objects.

Command Pattern Example:

Think of a restaurant scenario where customers place orders with waiters. The customers (senders) don’t directly interact with the kitchen (receiver), but give their orders to the waiters (command objects). The waiter writes down the order (command) and passes it to the kitchen staff (invoker), who will then execute the command (prepare the food).

Command Pattern C++ Example:

Let’s demonstrate the Command Pattern with a simple remote control that operates different electronic devices (TV and Stereo). The remote control can store and execute various commands (TurnOnCommand and TurnOffCommand).

Command Pattern Explanation of Code:

#include <iostream>
#include <vector>

// Receiver: Electronic Devices
class TV {
public:
    void turnOn() {
        std::cout << "TV is ON." << std::endl;
    }

    void turnOff() {
        std::cout << "TV is OFF." << std::endl;
    }
};

class Stereo {
public:
    void turnOn() {
        std::cout << "Stereo is ON." << std::endl;
    }

    void turnOff() {
        std::cout << "Stereo is OFF." << std::endl;
    }
};

// Command Interface
class Command {
public:
    virtual void execute() = 0;
};

// Concrete Commands
class TurnOnCommand : public Command {
private:
    TV* tv;

public:
    TurnOnCommand(TV* tv) : tv(tv) {}

    void execute() override {
        tv->turnOn();
    }
};

class TurnOffCommand : public Command {
private:
    TV* tv;

public:
    TurnOffCommand(TV* tv) : tv(tv) {}

    void execute() override {
        tv->turnOff();
    }
};

// Invoker: Remote Control
class RemoteControl {
private:
    std::vector<Command*> commands;

public:
    void addCommand(Command* command) {
        commands.push_back(command);
    }

    void pressButton(int index) {
        if (index >= 0 && index < commands.size()) {
            commands[index]->execute();
        }
    }
};

int main() {
    TV tv;
    Stereo stereo;

    TurnOnCommand turnOnTV(&tv);
    TurnOffCommand turnOffTV(&tv);
    TurnOnCommand turnOnStereo(&stereo);
    TurnOffCommand turnOffStereo(&stereo);

    RemoteControl remote;
    remote.addCommand(&turnOnTV);
    remote.addCommand(&turnOffTV);
    remote.addCommand(&turnOnStereo);
    remote.addCommand(&turnOffStereo);

    remote.pressButton(0); // Turns on TV
    remote.pressButton(3); // Turns off Stereo

    return 0;
}

Output:

TV is ON.
Stereo is OFF.

Command Pattern Explanation of the Code:

In this example, we have five main components:

  1. TV and Stereo are receiver classes, representing electronic devices that can be controlled.
  2. Command is the command interface that declares an execute method, which all concrete commands must implement.
  3. TurnOnCommand and TurnOffCommand are concrete command classes, each associated with a specific receiver (TV or Stereo). They implement the execute method to invoke the appropriate action on the receiver.
  4. RemoteControl is the invoker class, which holds a collection of commands and executes them when a button is pressed.

In the main function, we create instances of TV and Stereo, as well as different command objects for turning them on and off. We add these command objects to the RemoteControl. When we press a button on the remote (pressButton method), the corresponding command is executed, which, in turn, calls the appropriate action on the receiver (TV or Stereo).

Applications of Command Pattern:

  1. GUI applications: The Command Pattern can be used to implement undo/redo functionality.
  2. Multi-level menu systems: It can be used to handle menu navigation and actions.
  3. Transactional systems: In banking applications, commands can be used to represent transactions that can be committed or rolled back.

Command Pattern Pros and Cons:

Command Pattern Pros:

  • Decouples sender and receiver: The pattern promotes loose coupling between objects, making it easy to add new commands and receivers.
  • Supports undo/redo: It allows implementing undo and redo operations by storing command history.
  • Simplifies complex logic: It can encapsulate complex operations into reusable command objects.

Command Pattern Cons:

  • Increases the number of classes: Implementing the pattern can lead to an increase in the number of classes, which may make the codebase more complex.
  • Command storage: Storing command history for undo/redo functionality may consume additional memory.