Command Design Pattern

Learn via video courses
Topics Covered

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.

inroduction to Command Design Pattern

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.

Real-time use of command pattern

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.

Structure of command pattern

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

  1. 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.
  2. Create the Command interface that has only one method declaration, execute(), that should be implemented by its concrete implementation classes.
  3. 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
  4. Create the Invoker class and assign all the commands it can execute. The Invoker accepts the concrete command instances as the constructor parameters.
  5. 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.

Pseudo code of command design pattern

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.