Design Patterns: Encapsulate actions with the Command pattern
This is the fifth instalment of my series on design patterns in software. For the introduction to the whole series, definitely check out Design Patterns: Introduction with Singletons. This initial post describes general information about design patterns as well. The complexity of the command pattern is slightly lower compared to the previous decorator pattern, because there is less injection and wrapping going on.
The command pattern is a behavioural design pattern in which you can encapsulate all information you need to perform an action at a later moment in time. That object contains all information that needs to be passed on and a target object and method that has to be executed eventually.
In short, it encapsulates a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations. In the command pattern there always is a command, a receiver, an invoker and the client managing it all.
As per usual, a simple UML diagram showing the outline of this pattern:
As you can see, you can construct a RemoteControl object that holds a list of commands for (let's say) all the buttons it has. Each button invokes a different command in its commands array. It doesn't care about what command it executes, as each command has a different implementation with a different receiver.
First and foremost, you can have an invoker that can have multiple commands without knowing anything about any of those commands or the request from the command to the receiver. It shouldn't matter, this should be loosely coupled.
The second reason why people often use the command pattern, is the possibility of undoing a certain operation. Aside from the Execute() operation each command has, you could add an Undo() operation. This operation would then Undo() what Execute() has done.
Personally, I don't like the command pattern for undoing operations. Especially considering I often do rendering work and have much memory available. I prefer the memento and state patterns for this (more on this in later posts).
As per usual and with all behavioural patterns, you are making behaviour dynamic. The invoker class could expose methods that allow for customization of the commands array. Looking at the UML diagram above, this would mean you can realize a programmable remote control.
Even though I just mentioned that personally I don't like this pattern for undo operations, it does work fine. Some situations are simply too complex to be undone, however the command pattern is often implemented for undo functionality.
Once again, the open/closed principle comes back. This is one more design pattern that tries to make sure code doesn't have to be modified when you need to add behaviour. You can simply implement the command interface and add a few commands, add a receiver and push it in the commands array of the invoker. Almost no existing code will have to be modified, with the exception of the line you add for adding the command to the array.
Example in TypeScript
In this very extremely simple example, I have only one receiver and three concrete commands. A client, in this case the Application class, instantiates a Keyboard, a few receivers and the commands. The Keyboard in this example is the invoker and holds a map of commands for keyboard key codes. The LedLight receiver is a simple renderable that draws a led on a canvas. The LedOn, LedOff and LedToggle commands provide an interface between the Keyboard and the LedLight.
It is very easy to now add a new receiver (i.e. a led matrix, a rotating object or whatever you want to add) and commands that use that receiver. The client then simply registers these commands to the invoker and you have extended the behaviour of the application.
You could extend each command by adding an undo operation that negates the operation that was done in Execute(). For example, a command that increases the speed of some animation could have an undo that decreases that speed by the exact amount.
I have not drawn the relationships of the Application class to keep the UML diagram a little bit clear, but here it is:
Check out this pen.
I hope you can see an application for this pattern, even though it might not be that useful at all times. That is the case with all patterns though, you need different ones in every situation. I love feedback, so let me know if I need to change anything or adjust the format. I want this to be as educational and understandable as it can be! I'll write about the Composite pattern next, which could remind people of trees...