Learn the Command Design Pattern
This is the 19th post in a series on design patterns.
Command is a behavioral design pattern that turns a request into a standalone object that contains all information about the request. This transformation lets you parameterize methods with different requests, delay or queue a request’s execution, and support undoable operations.
Imagine you are developing a new text-editing application. The current task is to create a menubar with buttons that will allow you to perform various functions in the editor. There is a base class Button, which is extended by subclasses based on the actions they would take on a click event.
However, this approach is inherently flawed. The GUI code has become awkwardly dependent on the volatile code of the business logic. Some operations, like copying and pasting text, might need to be invoked from multiple locations. Users can, for example, click the copy button, copy using the context menu, or just hit Ctrl+C on the keyboard. Either you have to duplicate the operation’s code in numerous classes or you have to make menus dependent on buttons.
Command Design Pattern
According to the Command pattern, GUI objects should not directly send these requests. You should instead extract all of the request details, such as the object being called, the name of the method, and the arguments, into a separate command class with a single method that triggers this request.
GUI and business logic objects are linked by command objects. As a result, the GUI object won’t need to know what business logic object will receive the request or how it will be handled. It just triggers the command, which handles all the details.
UML Class Diagram
Not familiar with UML Class Diagram? I have written a detailed post on the UML Class diagram.
Implementation steps
- Declare a single execution method for the command interface.
- Extract requests into concrete command classes that implement the command interface. In each class, there must be a set of fields for storing request arguments and a reference to the receiver object. These values must all be initialized in the command’s constructor.
- Identify the classes that will act as senders. Incorporate fields for storing commands in these classes. Commands should only be sent via the command interface. Senders usually don’t create command objects on their own, but instead, get them from clients.
- Clients should initialize command objects in the following order:
* Create receivers
* Create commands and associate them with receivers if needed.
* Create senders and associate them with specific commands.
Source Code Implementation
In most cases, the Command interface declares just one method for executing the command.
A CopyCommand and PasteCommand class implements different kinds of requests. Concrete commands do not perform the work on their own, but rather pass the call to one of the business logic objects.
Menu (Sender) is responsible for initiating requests. A reference to a command object must be stored in this class. Instead of sending the request directly to the receiver, the sender triggers that command.
Business logic is contained in the Application (Receiver) class. In general, commands only handle how a request is passed to the receiver, while the receiver performs the actual work.
The CommandClient constructs and configures concrete command objects. The client must pass all request parameters, including a receiver instance, to the command’s constructor. Afterward, the resulting command may be associated with one or more senders.
// Output Copy Selected Text :Hello World Paste Copied Text : Hello World
When To Apply Command Design Pattern
- When you want to parameterize objects with operations, use the Command pattern.
- When you want to queue operations, schedule their execution, or execute them remotely, you should use the Command pattern.
- You can implement reversible undo/redo operations using the Command pattern.
Pros of Command Design Pattern
- Classes that invoke operations can be decoupled from classes that perform them.
- This lets you add new commands without breaking existing client code.
- Undo/redo can be implemented.
- You can implement deferred execution of operations.
- One can assemble a set of simple commands into a complex one.