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.
Table of Contents
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:
TV
andStereo
are receiver classes, representing electronic devices that can be controlled.Command
is the command interface that declares anexecute
method, which all concrete commands must implement.TurnOnCommand
andTurnOffCommand
are concrete command classes, each associated with a specific receiver (TV or Stereo). They implement theexecute
method to invoke the appropriate action on the receiver.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:
- GUI applications: The Command Pattern can be used to implement undo/redo functionality.
- Multi-level menu systems: It can be used to handle menu navigation and actions.
- 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.