Introduction
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.
#Command Pattern
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.
#But... why?
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).
#DYNAMIC BEHAVIOUR
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.
#UNDO
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.
#EXTENSION
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:
#Example real-life applications
- EntityFramework Migrations. Even though this might not seem the perfect example, each migration class added contains an Up() (Execute) and Down() (Undo) method. This way, EntityFramework can build a database based on a list of commands (migrations) and also easily undo all operations with the Down() methods. In this case, the undo feature of this pattern is extremely useful
- Game development. Configurable input devices, keyboards and key mapping, mouse button mapping, controllers, joysticks and so forth. They often use a form of the command pattern to bind the invoker (a keyboard or mouse) to a command (an action) that does something to a receiver (i.e. a weapon in game).
- It is often used as interface between an invoker and receiver where no common interface has been realized yet. This also means physical interfaces between controllers and a set of devices.
Disadvantage of Command Pattern
ReplyDeleteLike any other design decision, Command pattern also involves tradeoffs. If you have lot of commands, just like in a major GUI application e.g. IDE, you might end up with lots of small command classes.
Though similar functionality can be grouped into couple of classes with each method performing task for a particular command. By the way, using Command pattern result in readable, maintainable and extensible code, so this cost is worth paying.
Read more: http://javarevisited.blogspot.com/2016/05/command-design-pattern-in-java-example-code.html#ixzz4juMfQcRV
http://javarevisited.blogspot.in/2016/05/command-design-pattern-in-java-example-code.html
ReplyDelete