The Command Design Pattern is a behavioral design pattern that encapsulates a request as an object, thereby decoupling the sender of the request from the receiver and allowing flexible execution of operations.
Turns a request into an object so it can be passed, stored, queued, or undone easily.
Encapsulates the request as an object and decouples the invoker (sender) from the receiver (handler).
Example: A Remote Controller where a user sends commands like turning on lights, TV, speakers, or AC. The remote does not directly control the devices; instead, it passes command objects to them. Each device then executes the command independently, keeping the sender and receiver loosely coupled.
A User interacts with a Remote Controller and sends a command instead of directly controlling devices.
The remote acts as an invoker, forwarding command objects to different devices like Lights, TV, Speakers, and AC.
Each device acts as a receiver and executes its own command independently, keeping the system loosely coupled.
Real Life Software Applications
Some real-life software examples where the Command Pattern is commonly used are:
GUI Buttons and Menu: Each button or menu item (Copy, Paste, Save, Undo) is a command object. The framework doesn’t know the exact action — it just executes the command.
Job Queues: AWS SQS, RabbitMQ, Each job/task is wrapped in a command object. Queues hold commands and execute them later or distribute them across systems.
Database Transactions: Example are Hibernate, JPA, SQL transaction managers. Each query (INSERT, UPDATE, DELETE) is wrapped as a command. Transactions execute them sequentially, and rollback is possible if one fails.
Components
The Command Design Pattern consists of the following key components that work together to decouple the sender and receiver of a request
Command Interface: Declares the common method (e.g., execute()) for all commands.
Concrete Commands: Implement the interface and encapsulate specific actions (e.g., turn on TV).
Invoker: Initiates the command execution without knowing the details (e.g., remote control).
Receiver: Performs the actual operation defined by the command (e.g., TV, stereo).
Working
Using Remote Control Example
The remote control acts as the Invoker
Each button is a Command object
The device (TV, AC, Light) is the Receiver
When a button is pressed, the command’s execute() method is called, which triggers the action on the receiver
Uses
This pattern is used when a request needs to be encapsulated as an object for flexible execution.
Implementing remote controls and button-based actions
Supporting undo and redo operations (editors, drawing tools)
Logging and storing command history
Implementation Example
Problem Statement
Imagine you are tasked with designing a remote control system for various electronic devices in a smart home. The devices include a TV, a stereo, and potentially other appliances. The goal is to create a flexible remote control that can handle different types of commands for each device, such as turning devices on/off, adjusting settings, or changing channels.
Challenges while implementing this system
Devices can have different functionalities, so designing a remote control that can easily handle different device types with varying functionalities without becoming highly complex.
Implementing a remote control that supports various commands without tightly coupling ensuring the remote control can execute commands for different devices without needing extensive modifications for each new command.
Designing a system that allows users to customize the behavior of the remote control dynamically.
Using the Command Pattern to Overcome These Challenges
The Command Pattern can be employed to address these challenges. It introduces a level of abstraction between the sender of a command (remote control) and the receiver of the command (electronic devices).
The Command Pattern decouples the sender (Invoker) from the receiver (Devices).
The remote control doesn't need to know the specific details of how each device operates; it only triggers commands.
New devices or commands can be added without modifying existing code.
The remote control can work with any device that implements the common command interface.
The Command interface declares a method, often named execute(). This method is meant to encapsulate a specific operation. The interface sets a contract for concrete command classes, defining the execute() method that encapsulates the operation to be performed.
2. Concrete Command Classes
Concrete command classes implement the Command interface. Each class encapsulates a specific operation related to devices. Each concrete command class provides a specific implementation of the execute() method, defining how a particular device operation (turning on, turning off, adjusting volume, changing channel) is executed.
3. Receiver Classes (Devices)
The Device interface declares methods related to device functionality, such as turnOn() and turnOff().This interface sets a contract for device classes, defining common operations that concrete devices should support.
4. Invoker Class (Remote Control):
The invoker class holds a reference to a Command object and triggers its execution through the execute() method. The invoker doesn't know the specific details of the command or the devices. It simply calls the execute() method on the current command, allowing for flexible and dynamic control over different devices.
Complete code for the above example
Below is the complete code for the above example:
Output
TV is now on
Volume adjusted
Channel changed
TV is now off
Advantages
This pattern offers several benefits that improve flexibility and maintainability of the system.
Decouples the sender from the receiver
Easy to add new commands without modifying existing code
Supports undo and redo operations
Allows commands to be logged or queued
Disadvantages
Despite its benefits, the pattern also introduces some drawbacks.