Command Pattern | C++ Design Patterns
Last Updated :
15 Jul, 2024
The Command Pattern is a behavioral design pattern that focuses on encapsulating a request as an object, thereby decoupling the sender of the request from the receiver. This pattern allows you to parameterize objects with commands, delay or queue a request's execution, and support undoable operations. It's a fundamental pattern for implementing a wide range of functionality in software systems.
Important Topics for the Command Pattern in C++ Design Patterns
Key components of the Command Pattern
1. Command Interface or Abstract Class
This is an interface or abstract class that declares an execute method, defining the contract for concrete commands. It ensures that all commands have a method to perform their actions.
2. Concrete Command
Concrete command classes implement the Command interface or inherit from the abstract class. Each concrete command encapsulates a specific action to be performed. For instance, you might have concrete commands to turn on lights, open files, or send messages.
3. Receiver
The receiver is responsible for performing the actual work when a command is executed. It knows how to carry out the requested action. For example, in a home automation system, the receiver could be a "Light" object that understands how to turn on and off.
4. Invoker
The invoker is responsible for triggering the execution of commands. It holds references to the commands and can execute them. It acts as an intermediary between the sender (client) and the receiver, ensuring that the sender remains decoupled from the receiver.
Command Pattern Examples in C++
Problem statement: Design a system that demonstrates the use of the Command Pattern to decouple the sender and receiver of a request. The system should consist of several key components: Command, Concrete Command, Receiver, and Invoker.
Below is the implementation of the Command Pattern in C++:
C++
#include <iostream>
// Receiver
class Receiver {
public:
// Receiver class defines the action to be performed.
void performAction()
{
std::cout << "Receiver is performing an action" << std::endl;
}
};
// Command interface
class Command {
public:
// The execute method is declared in the Command
// interface.
virtual void execute() = 0;
};
// Concrete Command
class ConcreteCommand : public Command {
private:
Receiver &receiver;
public:
// ConcreteCommand takes a reference to a Receiver
// object in its constructor.
ConcreteCommand(Receiver&rec)
: receiver(rec)
{
}
// The execute method calls the action on the Receiver.
void execute() { receiver.performAction(); }
};
// Invoker
class Invoker {
private:
Command* command;
public:
// The setCommand method allows setting the command to
// be executed.
void setCommand(Command* cmd) { command = cmd; }
// The executeCommand method triggers the execution of
// the command.
void executeCommand() { command->execute(); }
};
int main()
{
// Create a Receiver instance.
Receiver receiver;
// Create a ConcreteCommand, passing the Receiver to it.
ConcreteCommand command(receiver);
// Create an Invoker.
Invoker invoker;
// Set the command to be executed by the invoker.
invoker.setCommand(&command);
// Execute the command.
invoker.executeCommand();
return 0;
}
OutputReceiver is performing an action.
Problem statement: Design a system to simulate a remote control for various electronic devices. The system should allow users to turn these devices on and off using a remote control with multiple buttons. Implement the Command Pattern to achieve this functionality.
Below is the step by step process to implement above problem statement:
- Create an interface or base class Command that defines a execute method, which should be implemented by concrete commands.
- Implement concrete command classes, such as TurnOnCommand and TurnOffCommand, to encapsulate specific actions for turning electronic devices on and off.
- Create a receiver class ElectronicDevice for the electronic devices that need to be controlled. It should have methods to turn the device on and off.
- Design an Invoker class RemoteControl to manage and execute the commands. It should provide the following features:
- The ability to add commands to specific buttons on the remote control.
- A method to press a button, which triggers the associated command's execution.
- Demonstrate the functionality by creating instances of electronic devices (e.g., TV, lights), concrete commands (turn on TV, turn off lights), and a remote control.
- Users should be able to press the buttons on the remote control to turn electronic devices on and off.
- Ensure that the system is flexible and extensible, allowing for easy addition of new electronic devices and commands without modifying existing code.
Below is the implementation of the above example:
C++
#include <iostream>;
#include <vector>;
// Receiver: Electronic Device
class ElectronicDevice {
private:
std::string name;
public:
ElectronicDevice(const std::string & n)
: name(n)
{
}
void turnOn()
{
std::cout <<" is ON now " << std::endl;
}
void turnOff()
{
std::cout << " is OFF now" << std::endl;
}
};
// Command interface
class Command {
public:
virtual void execute() = 0;
};
// Concrete Command: Turn on
class TurnOnCommand : public Command {
private:
ElectronicDevice& device;
public:
TurnOnCommand(ElectronicDevice& dev)
: device(dev)
{
}
void execute() { device.turnOn(); }
};
// Concrete Command: Turn off
class TurnOffCommand : public Command {
private:
ElectronicDevice& device;
public:
TurnOffCommand(ElectronicDevice&dev)
: device(dev)
{
}
void execute() { device.turnOff(); }
};
// Invoker: Remote Control
class RemoteControl {
private:
std::vector <Command*> commands;
public:
void addCommand(Command* cmd)
{
commands.push_back(cmd);
}
void pressButton(int slot)
{
if (slot >= 0 && slot < commands.size()) {
commands[slot]->execute();
}
}
};
int main()
{
// Create electronic devices
ElectronicDevice tv("TV");
ElectronicDevice lights("Lights");
// Create commands for turning devices on and off
TurnOnCommand turnOnTV(tv);
TurnOffCommand turnOffTV(tv);
TurnOnCommand turnOnLights(lights);
TurnOffCommand turnOffLights(lights);
// Create a remote control
RemoteControl remote;
// Set commands for remote buttons
remote.addCommand(&turnOnTV); // Button 0: Turn on TV
remote.addCommand(&turnOffTV); // Button 1: Turn off TV
remote.addCommand(&turnOnLights); // Button 2: Turn on Lights
remote.addCommand(&turnOffLights); // Button 3: Turn off Lights
// Press buttons on the remote
remote.pressButton(0); // Turn on TV
remote.pressButton(3); // Turn off Lights
remote.pressButton(1); // Turn off TV
remote.pressButton(2); // Turn on Lights
return 0;
}
OutputTV is now ON.
Lights is now OFF.
TV is now OFF.
Lights is now ON.
Advantages of the Command Pattern in C++ Design Patterns
- Decoupling of Sender and Receiver: The Command Pattern decouples the sender of a request from the receiver, meaning that the sender does not need to know the specifics of how a request is handled. This decoupling promotes a more flexible and maintainable codebase.
- Command Queueing: Commands can be easily queued, which allows for the implementation of features like undo and redo functionality. This is particularly useful in applications where the order of execution matters.
- Logging and Auditing: Since each command is encapsulated as an object, it becomes straightforward to log and audit the commands that are executed. This is valuable for tracking system behavior and debugging.
- Extensibility: The Command Pattern makes it easy to add new commands and expand the functionality of an application without altering existing code. You can introduce new command classes without affecting the existing classes.
- Support for Composite Commands: By creating composite commands (commands that contain other commands), you can implement complex operations. This is helpful when you need to execute a sequence of actions as a single command.
- Encapsulation of State: Commands can encapsulate the state required for their execution. This encapsulation ensures that a command has all the information it needs to perform its action.
Disadvantages of the Command Pattern in C++ Design Patterns
- Code Complexity: Implementing the Command Pattern may lead to a more extensive class hierarchy, particularly when dealing with various types of commands and receivers. This added complexity can be overwhelming for simple systems.
- Increased Memory Usage: As each command is represented as an object, there can be increased memory overhead, especially when dealing with a large number of commands. This might not be suitable for memory-constrained systems.
- Potential Performance Overhead: The use of commands and their execution can introduce a slight performance overhead due to the indirection involved in encapsulating requests as objects. In performance-critical applications, this overhead may be a concern.
- Learning Curve: Understanding and implementing the Command Pattern can be challenging for developers who are new to design patterns. It may require additional training and experience to use it effectively.
- Limited Use Cases: The Command Pattern is not always the best choice for every scenario. It is most valuable in systems where you need to decouple senders and receivers, implement undo/redo functionality, or log commands. In simpler systems, it might be unnecessary.
Similar Reads
Modern C++ Design Patterns Tutorial
Design patterns in C++ help developers create maintainable, flexible, and understandable code. They encapsulate the expertise and experience of seasoned software architects and developers, making it easier for newer programmers to follow established best practices. What are C++ Design Patterns?A des
7 min read
Creational Software Design Patterns in C++
Factory Method Pattern | C++ Design Patterns
Factory Method Pattern provides an interface for creating objects but leaves the actual object instantiation to derived classes. This allows for flexibility in object creation and promotes loose coupling between the client code and the concrete products. Table of Content What is the Factory Method D
8 min read
Abstract Factory Pattern | C++ Design Patterns
Abstract Factory Pattern is a creational design pattern used in object-oriented programming. It provides an interface for creating families of related or dependent objects without specifying their concrete classes. This pattern is a way to encapsulate the creation of objects and ensure that they are
6 min read
Builder Pattern | C++ Design Patterns
The builder pattern is defined as a creational design pattern that separates the construction of a complex object from its representation, allowing us to create different representations of an object using the same construction process. It's beneficial when an object has many optional properties or
6 min read
Prototype Pattern | C++ Design Patterns
When designing software, it's crucial to make it efficient, easy to reuse, and simple to maintain. One way to achieve these goals is by using design patterns, and one such pattern is the Prototype Pattern. In this article, we'll explore the Prototype Design Pattern in the context of C++. Important T
5 min read
Singleton Pattern | C++ Design Patterns
A singleton pattern is a design pattern that ensures that only one instance of a class can exist in the entire program. This means that if you try to create another instance of the class, it will return the same instance that was created earlier. The Singleton pattern is useful when we need to have
11 min read
Structural Software Design Patterns in C++
Adapter Pattern | C++ Design Patterns
Adapter Pattern is a structural design pattern used to make two incompatible interfaces work together. It acts as a bridge between two incompatible interfaces, allowing them to collaborate without modifying their source code. This pattern is particularly useful when integrating legacy code or third-
6 min read
Bridge Method | C++ Design Patterns
Bridge Pattern is basically a structural design pattern in software engineering or in C++ programming that is used to separate an object's abstraction from its implementation. It is part of the Gang of Four (GoF) design patterns and is particularly useful when we need to avoid a permanent binding be
9 min read