What is Command Design Pattern ?
Command design pattern is a behavioral pattern in which object are use to encapsulate all the information that need to perform action or trigger an event at later time. This information includes the method name, the object that owns the method and values for the method parameters.
- Encapsulate a request in an object
- Allows the parameterization of clients with different requests
- Allows saving the requests in a queue
There are four terms associate with the command pattern. They are:
- Command: This element contains information about the necessary action to be taken. It calls the required method from receiver.
- Receiver: This element contains the actual implementation (knows how to carry out requested command) of any commands. This also maintains the history of executed commands.
- Invoker: This element initiates the whole process. It takes arguments from the client and invokes the process to call the required command.
Client: This element really behaves like a client (deciding what to do). Its job is to determine which command to execute, without knowing who will execute it and how it will be executed.
# Invoker class Switch attr_reader :history def execute(cmd) @history ||=  @history << cmd.execute end end # Command Interface class Command attr_reader :light def initialize(light) @light = light end def execute raise NotImplementedError end End # Command for turning on class TurnOnCommand < Command def execute light.turn_on end end # Command for turning off class TurnOffCommand < Command def execute light.turn_off end end # Receiver class Light def turn_on 'the light is on' end def turn_off 'the light is off' end end # Client class LightSwitchClient attr_reader :switch def initialize @lamp = Light.new @switch = Switch.new end def switch_for(cmd) case cmd when 'on' then @switch.execute(TurnOnCommand.new(@lamp)) when 'off' then @switch.execute(TurnOffCommand.new(@lamp)) else puts 'Sorry, I so sorry' end end end client = LightSwitchClient.new puts client.switch_for('on') puts client.switch_for('off') puts client.switch.history
'the light is on' 'the light is off' ['the light is on', 'the light is off']
In above Example:
Switch class executing the commands and keeping the log of the commands. Command interface accepting the parameter is light in this case. We have two concrete command classes ( TurnOnCommand and TurnOffCommand ). These command classes invokes the methods of the receiver. Receiver does the actual works. Receiver’s job is to turn on the light or turn off the light. Client makes the decision which command should created and when to execute the commands.
Specific problems and implementation
Now that we have understood how the pattern works, it’s time to take a look at its advantages and flaws, too.
The intelligence of a command
There are two extremes that a programmer must avoid when using this pattern:
- The command is just a link between the receiver and the actions that carry out the request
- The command implements everything itself, without sending anything to the receiver.
We must always keep in mind the fact that the receiver is the one who knows how to perform the operations needed, the purpose of the command being to help the client to delegate its request quickly and to make sure the command ends up where it should.
Undo and redo actions
As mentioned above, some implementations of the Command design pattern include parts for supporting undo and redo of actions. In order to do that a mechanism to obtain past states of the Receiver object is needed; in order to achieve this there are two options:
Before running each command a snapshot of the receiver state is stored in memory. This does not require much programming effort but can not be always applied. For example doing this in an image processing application would require storing images in memory after each step, which is practically impossible.
Instead of storing receiver objects states, the set of performed operations are stored in memory. In this case the command and receiver classes should implement the inverse algorithms to undo each action. This will require additional programming effort, but less memory will be required. Sometimes for undo/redo actions the command should store more information about the state of the receiver objects. A good idea in such cases is to use the Memento Pattern.
Asynchronous Method Invocation
Another usage for the command design pattern is to run commands asynchronously in background of an application. The invoker runs in the main thread and sends the requests to the receiver which runs in a separate thread. The invoker will keep a queue of commands to be run and will send them to the receiver while it finishes running them.
Instead of using one thread in which the receiver is running more threads can be created for this. But for performance issues (thread creation is consuming) the number of threads should be limited. In this case the invoker will use a pool of receiver threads to run command asynchronously.
Using composite commands
When adding new commands to the application we can use the composite pattern to group existing commands in another new command. This way, macros can be created from existing commands.
The main advantage of the command design pattern is that it decouples the object that invokes the operation from the one that know how to perform it. And this advantage must be kept. There are implementations of this design pattern in which the invoker is aware of the concrete commands classes. This is wrong making the implementation more tightly coupled. The invoker should be aware only about the abstract command class.
Suppose a program has a sequence of commands that it executes in order. If each command object has a get_estimated_duration() method, the program can easily estimate the total duration. It can show a progress bar that meaningfully reflects how close the program is to completing all the tasks.
If all user actions are represented by command objects, a program can record a sequence of actions simply by keeping a list of the command objects as they are executed. It can then “play back” the same actions by executing the same command objects again in sequence. If the program embeds a scripting engine, each command object can implement a to_script() method, and user actions can then be easily recorded as scripts.