Command Design Pattern
Overview
Command design pattern is a behavioral design pattern (communication pattern among objects) that converts a request or an operation into an object with all the required information such as what method to call, arguments, etc.
When Will We Need Command Design Pattern?
Assume a front-end team responsible for creating UI (User Interface) components such as button, dropdown, checkbox, etc. These components can then be used to build UI for any application. For example, the button component can be used in a calculator application.
The frontend team is unaware of the action that will be executed when a button is clicked. It is up to the application team to decide what action will be executed when a button is clicked. This is where the command design pattern comes in. With command design pattern, the executor of the command (button component) does not need to know anything about the command being executed. So, any action can be attached to a button component. For example, execute add operation when + button is clicked and execute subtract operation when- butting is clicked. We implement command design pattern by creating separate command classes.
How Does Command Design Pattern Work?
Consider we have a universal remote with four buttons On, Off, Up, and Down. This remote can be configured for any device such as a light, speaker, air conditioner, and the buttons can be configured for the respective device's use-cases.
Example
- The Up/Down button can be configured to increase/decrease the brightness if the device is a light.
- The Up/Down button can be configured to increase/decrease the volume if the device is a speaker.
In command design patterns, we create command objects for the above-mentioned On, Off, Up, and Down commands.
Structure
Command design patterns have a few core participants such as Invoker, Receiver, ConcreteCommand, etc., that are responsible for executing operations based on the client's request.
Receiver Receives command from the ConcreteCommand and knows how to perform operations associated with the command. E.g., Light, Speaker.
Command An interface declared with an execute() method that individual commands would implement. It can also have an unexecute() method for unexecuting the last executed operation. For the sake of simplicity, we leave unexecute() out of the picture and focus just on the execute() method.
ConcreteCommand Implements the Command interface and provides implementation for the execute() method. It communicates with the receiver to trigger appropriate action. Eg. OnCommand, OffCommand.
Invoker Responsible for executing the command assigned by the Client based on the incoming request.
Client Responsible for creating the ConcreteCommand, setting its receiver, and assigning it to the Invoker. E.g. RemoteApplication.
Implementation
- First step is to create the Receiver classes. In our use case, a receiver can be any device (speaker, light, etc.) that receives the command and performs the operation. The remote can be configured to control any device.
- Create the Command interface that has only one method declaration, execute(), that should be implemented by its concrete implementation classes.
- Create the concrete command classes for all four buttons (On, Off, Up, and Down) that implement the Command interface. The concrete command classes accept a Device instance in the constructor on which the action will be executed when the command is invoked
- Create the Invoker class and assign all the commands it can execute. The Invoker accepts the concrete command instances as the constructor parameters.
- Create the client class RemoteApplication responsible for creating the concrete command objects and assigning them to the Invoker.
Pseudocode
We will implement command design pattern for the remote application discussed in the above section.
Receiver
We create two devices, Light and Speaker. We create the Light and the Speaker classes that implement the Device interface.
Pseudocode
Device
Light
Speaker
Java
Device.java
Light.java
Speaker.java
CPP
device.cpp
light.cpp
speaker.cpp
Python
device.py
light.py
speaker.py
Command Interface
We create the Command interface which will be implemented by the concrete command classes.
Pseudocode
Command
Java
Command.java
CPP
command.cpp
Python
command.py
Concrete Commands
Lets create the concrete commands for the functionalities on, off, up and down.
Pseudocode
OnCommand
OffCommand
UpCommand
DownCommand
Java
OnCommand.java
OffCommand.java
UpCommand.java
DownCommand.java
CPP
on_command.cpp
off_command.cpp
up_command.cpp
down_command.cpp
Python
on_command.py
off_command.py
up_command.py
down_command.py
Invoker
Now we create the Invoker class and assign all the commands it can execute. The Invoker accepts the concrete command instances as the constructor parameters. The public methods of the Invoker are configured to call the execute() method of the respective commands.
Pseudocode
Invoker
Java
Invoker.java
C++
invoker.cpp
Python
invoker.py
Client
Finally, we create the client class RemoteApplication responsible for creating the concrete command objects and assigning them to the Invoker. This class can be considered as the main Java file.
Pseudocode
RemoteApplication
Java
RemoteApplication.java
C++ remote_application.cpp
Python
remote_application.py
In the above class, we created the Light instance and use it to create all the command instances, which are then configured through the Invoker.
Output
Explanation Whenever a method of Invoker instance is called, it calls the execute() method of the associated command, which then calls the associated action of the Light receiver.
If we want to configure the remote for the speaker, we can just pass the Speaker instance to concrete commands while configuring the Invoker like below.
Output
When we run the above class, we get the below output:
Explanation Whenever any method of the Invoker instance is called, it calls the execute() method of the associated command, which then calls the associated action of the Speaker receiver.
Pros and Cons of Command Design Pattern
Pros
1. Loose Coupling Command design pattern loosely couples the object that invokes an operation and the object that performs the operation.
2. Undo/Redo Operations Command design pattern provides the ability to undo and redo a command. Each concrete command object can have a unexecute() method that can undo an operation. A separate stack can be maintained to keep track of the executed command so that it can be used to undo/redo an operation.
3. Extensibility Adding a new command is easy. A concrete command class must be written and passed to the invoker. Command design pattern leverages the Open/Closed principle.
Cons
1. Growing Classes Command design pattern insists on creating a separate class for each command is too much and may cause difficulty maintaining the codebase.
Difference between Command Pattern and Strategy Pattern
- Command design pattern is used to convert a request/command into an object by encapsulating it with all required data like a method to call, arguments, etc. Every concrete command performs a specific action. For example, On command to turn on device and Off command to turn off device.
- Strategy design pattern is used to when there are multiple algorithms for the same task, and we want to pick the best one at runtime. For example, CardStrategy and NetBankingStrategy are used for same functionality (i.e) debit money from account.
FAQs
Q: When should the command design pattern be used?
A: Command design pattern should be used when the caller of an operation doesn't necessarily need to know how to perform the operation. Example: A button UI component doesn't need to know how to perform the operation when it is clicked.
Q: Does command design pattern provides extensibility?
A: Yes, the command design pattern provides extensibility. Adding a new command is as easy as adding a new class. It encourages the Open/Closed principle.