Let us focus on the non-implementation aspect of the command design, and some main reasons for using the Command desing pattern grouped in two major categories:
- Hiding actual implementation of how the command is executed
- Allow methods to be built around command, aka command extensions
Hide implementation
In most programming, you'll want to hide away implementation so that when looking at the top-most problem, it consists of a comprehensible subset of commands/code. I.e. You don't need/want to know the gory details of how a light is switched on, or a car is started. If your focus is to get the car started, you don't need to understand how the engine works, and how it needs fuel to enter the engine, how the valves work, ...
Indicating the action, not how it is done
A command gives you this kind of view. You'll immediately understand what the TurnLightOn
command does, or StartCar
. Using a command you'll hide the details of how something is done, whilst clearly indicating the action which is to be executed.
Allow changing of inner details
In addition, lets say you later on rebuild your entire Light
class, or a Car
class, which requires you instantiate several different objects, and possibly you need to something else before actually doing your wanted operation. In this case if you had implemented the direct access method, in a lot of places, you would need to change it in all places where you've coded it beforehand. Using a command, you can change the inner details of how to do stuff without changing the calling of the command.
Possible command extensions
Using a Command interface gives you an extra layer between the code using the command, and the code doing the actual action of the command. This can allow for multiple good scenarios.
Security extension, or interface exposure
Using a command interface, you can also limit access to your objects, allowing you to define another level of security. It could make sense to have a module/library with a fairly open access so that you could handle internal special cases easily.
From the outside, however, you might want to restrict the access to the light so that it is only to be turned on or off. Using commands gives you the ability to limit the interface towards a class.
In addition if you want, you could build a dedicated user access system around the commands. This would leave all of your business logic open and accessible and free of restrictions, but still you could easily restrict access at the command level to enforce proper access.
Common interface to executing stuff
When building a sufficient large system, commands gives a neat way to bridge between different modules/libraries. Instead of you needing to examine every implementation detail of any given class, you can look into which commands are accessing the class.
And since you are leaving out implementation details to the command itself, you could use a common method to instantiate the command, execute it and review the result. This allows for easier coding, instead of needing to read up on how to instantiate that particular Light
or Car
class, and determining the result of it.
Sequencing of commands
Using commands, you can also do stuff like sequencing of commands. That is since you don't really matter if you are executing the TurnOnLight
or StartCar
command, you can execute sequences of theses as they are executed the same way. Which in turns can allow for chains of commands to be executed, which could be useful in multiple situation.
You could build macros, execute sets of commands which you believe are grouped together. I.e. the command sequence: UnlockDoor
, EnterHouse
, TurnOnLight
. A natural sequence of commands, but not likely to made into a method as it uses different objects and actions.
Serialisation of commands
Commands being rather small in nature, also allows for serialisation quite nicely. This is useful in server-client context, or program-microservice context.
The server (or program) could trigger a command, which then serialises the command, sends it over some communication protocol (i.e. event queue, message queue, http, ... ) to someone actually handling the command. No need to first instantiate the object at the server, i.e. a Light
which could be light-weight (pun intended), or a Car
which could be a really large structure. You just need the command, and possibly a few parameters.
This could be a good place to introduce to the CQRS - Command Query Responsibility Separation pattern for further studies.
Tracking of commands
Using the extra layer of commands in your system could also allow for logging or tracking commands, if that is a business need. Instead of doing this all over the place, you can gather tracking/logging within the command module.
This allows for easy changes of logging system, or addition of stuff like timeing, or enabling/disabling logging. And it's all easily maintained within the command module.
Tracking of commands also allows for allowing undo actions, as you can choose to re-iterate the commands from a given state. Needs a little bit of extra setup, but rather easily doable.
In short, commands can be really useful as they allow connection between different parts of your soon-to-be-large program, as they are light-weight easily remembered/documented and hides the implementation details when so needed. In addition the commands allows for several useful extensions, which when building a larger system will come in handy: i.e. common interface, sequencing, serialisation, tracking, logging, security.