As you probably know, in software engineering, a design pattern is a general repeatable solution to a commonly occurring problem in software design. A design pattern can't be transformed directly into code but instead it is a description or template for how to solve a problem that can be used in many different situations. Also a design pattern can be improved over time, to make it more robust.
Design patterns fall into three classifications:
- Creational design patterns : These are all about class instantiation.
- Structural design patterns : These are all about Class and Object composition.
- Behavioral design patterns : These are all about Class's objects communication.
In this article, let's focus on the Command Pattern which is a behavioral design pattern.
Command Pattern
As mentioned in the popular reference book, Design patterns Elements of Reusable Object-Oriented Software by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides
Command pattern encapsulates a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations.
Let's dive deep by examples
1. Non-software example
Let's think of a situation where a customer enters into a restaurant and a waiter comes around to his table to take the order. The waiter writes down the order on a check and the order is queued till the cook has time to work on it.
This is exactly what the above definition is describing if you think about it and If we try to reflect this example in the software application instances, we will have:
- Customer(Client) : who initiates the request.
- Waiter(Invoker) : who encapsulates the request by writing it on the check.
- Order(Command) : which is the request that is encapsulated as an object.
- Cook(Receiver) : who receives and executes the command.
2. Software example
Let's take a very simple example of a calculator app.
Simple calculator without using command pattern (above) vs Simple calculator using command pattern (below)
The key difference here is that we introduced an interface between the client and the command receiver and through the implementations of that interface, we will encapsulate the client request as an object before it arrives to the receiver which is pretty much similar to the first example.
Some people including myself (when I was going through this pattern) can ask what is the advantage of doing so? Also, It is obvious that it will add complexity in our code.
Why would you care at all ?
With the first example(Non-software), it is obvious. If we remove the command interface, that means we don't have a waiter between a customer and a cook. Thus, the customer communicates what he wants directly to the cook. Therefore, they are many things would go wrong. For instance, a cook would waste a lot of time to write down orders or he might forget to work on some orders especially if there is a large number of customers.
With the second example(Software), it is not very simple to clearly explain the advantage of the command pattern.
Let's break this down with a stack implementation example.
If you don't use the command pattern, you can write your implementation by simply having an Application class call the Stack class for popping or/and pushing a value.
Now, what if we want to add undo/redo functionality in our stack implementation? In this case, to do the implementation using command pattern can allow us to add this functionality easily especially if we care about the SOLID design principles (eg: open-closed principle) and we should.
With the command pattern implementation below, we can take every stack operation method and create a concrete command class for that. This mean creating a PushCommand and a PopCommand.
Thus, when we add undo/redo functionality in our stack implementation, we can only add the unExecute() method in each command. As a result, execute() in the PushCommand calls push() in the Stack class , unExecute() in the PushCommand calls pop() in the Stack class, execute() in the PopCommand calls pop() in the Stack class and finally unExecute() in the PopCommand calls push() in the Stack class.
Conclusion
Command design pattern is a behavioral design pattern. We use it to encapsulate all the information required to trigger an event. Some of the main uses of Command pattern are:
Graphic User Interface (GUI): clicking a button we can read the current information of GUI and take an action.
Transactions: In a transactional behavior code there are multiple tasks/updates. When all the tasks are done then only transaction is committed. Else we have to rollback the transaction. In such a scenario each step is implemented as separate Command.
There are also issues with command pattern that you might need to think of before using it:
- If you have many methods in the Receiver class, you can end up creating many Command classes.
- How to store the state in the command object such that we can perform an undo by calling unExecute() especially when the state is little complex.