In the series of behavioral design patterns, the Command Pattern is the one that encapsulates method invocation. We can issue requests to objects without knowing anything about the operation being requested or the receiver of the request. Let’s go into detail and see what it is:
The Command Pattern
The Command Pattern encapsulates a request as an object, thereby letting you parameterize clients with various requests, queue or log requests, and support undoable operations. (Gamma et. al.).
Uses of The Command Pattern
Let’s understand it with an example of the use of the Command Pattern.
I will use the same example I did in my first blog; The Remote Control. A company that manufactures the remote control, has several vendors (classes). These vendors have their own set of methods and interfaces to be invoked, such as On, Off, Dim, IncreaseVolumne, DecreaseVolume and so on and so forth. There are not many common interfaces among several vendor classes other than ON or OFF.
The manufacturing company can expect more diverse classes in the future with just as diverse methods. A bad design for the problem would be:
If(slot1== TV), then light.On(),
else if(slot1==Washmachine), then washmachine.On()
One way of analyzing the preceding problem is to follow the Separation of Concerns Principle: The remote should know how to interpret button presses and make requests, but it should not know a lot about home automation.
Goals of a good design for this situation:
- Separate the remote’s ability to execute a command from the variety of operations and devices that can be supported.
- We should analyze our problem with the following two questions:
- What varies?
Devices and operations.
- What stays the same?
The structure of the remote control.
UML Diagram and participants: For the sake of a better understanding, I have mapped participants in the pattern with probable classes in my example.
Participants
The classes and/or objects participating in this pattern are:
- Command (Command)
– declares an interface for executing an operation.
- ConcreteCommand (SwitchOnWaschmachine)
– defines a binding between a Receiver object and an action.
– implements Execute by invoking the corresponding operation(s) on Receiver.
- Client (CommandApp)
– creates a ConcreteCommand object and sets its receiver.
- Invoker (Remote)
– asks the command to carry out the request.
- Receiver (Waschmachine, Fernseher, Microwelle)
– knows how to do the operations associated with carrying out the request.
Sequence Diagram
A Sequence Diagram shows how multiple participants communicates among each other.
Step-by-step analysis:
- The client creates a ConcreteCommand object and specifies its receiver.
- An invoker object stores the concreteCommand object.
- The Invoker issues a request by calling Execute on the command. When commands are undoable, ConcreteCommand stores for undoing the command prior to invoking Execute.
- The concreteCommand object invokes operations on its receiver to carry out the request.
As it is said, if you are able to execute an action, you should also know how to undo the executed action. The same applies to our example too. If a household is started, the undo button should be able to shut it down. Or let’s say any last executed action should be reset when the undo button is pressed.
How to undo it
- Add undo() to the Command interface.
- Each ConcreteCommand must know how to undo the action it executed.
- Make the Remote remember the last command executed and call it’s undo method for the undo button.
- How does a remote remember a previously executed command?
- Undo for.
- Oven: simple (does an Oven need the hardcoded previous state on/off?).
- Washing machine: ask the device for the state before execution.
Command Pattern in action
Abstract Command class:
- public abstract class ICommand
- {
- public abstract void Exceute();
-
- public abstract void Undo();
- }
Concrete Receivers
An Oven and Washing machine are receivers in our examples. They only know how can they be switched on or switched off.
Oven
- public class Oven
- {
-
- internal void SwitchOff()
- {
- MessageBox.Show("Oven is off");
- }
-
- internal void SwitchOn()
- {
- MessageBox.Show("Oven is on");
- }
- }
-
-
- public class Waschmachine
- {
-
- internal void SwitchOn()
- {
- MessageBox.Show("Switching On Washing Machine");
- }
-
-
- internal void SwitchOff()
- {
- MessageBox.Show("Switching Off Washing Machine");
- }
- }
Concrete CommandsConcrete commands implement abstract command interfaces. The Execute method in a concrete class encapsulates method invocation (_waschmachine.SwitchOn()). Concrete commands also know their receivers.
- public class SwitchOnWaschmachine:ICommand
- {
- private readonly Waschmachine _waschmachine;
-
- public SwitchOnWaschmachine(Waschmachine waschmachine)
- {
- _waschmachine = waschmachine;
- }
-
- public override void Exceute()
- {
- _waschmachine.SwitchOn();
- }
-
-
- public override void Undo()
- {
- _waschmachine.SwitchOff();
- }
-
- public override string ToString()
- {
- return "Washing Machine";
- }
- }
Also, observe that each concrete command knows how to undo its original operation. One command knows how to switch it off and vice versa.
- public class SwitchOffWaschmachine :ICommand
- {
- private readonly Waschmachine _waschmachine;
-
- public SwitchOffWaschmachine(Waschmachine waschmachine)
- {
- _waschmachine = waschmachine;
- }
-
- public override void Exceute()
- {
- _waschmachine.SwitchOff();
- }
-
-
- public override void Undo()
- {
- _waschmachine.SwitchOn();
- }
-
- public override string ToString()
- {
- return "Washing Machine";
- }
- }
Invoker
A remote plays the role of an invoker and only knows how to execute a command but it does not know who the receiver of the command is and how the command is actually executed. In that way, the receiver and invoker are separated from each other.
Switch On click button
- private void SwitchOn_Click(object sender, EventArgs e)
- {
- Button btn = sender as Button;
-
- int col = btn != null ? Grid.GetColumn(btn) : -1;
- if (col == 1)
- {
- var slot = btn != null ? Grid.GetRow(btn) : -1;
- _onCommands[slot].Exceute();
-
-
- _unDoCommand = _onCommands[slot];
- }
-
- }
Switch Off click button
- private void SwitchOff_Click(object sender, EventArgs e)
- {
- Button btn = sender as Button;
-
- int col = btn != null ? Grid.GetColumn(btn) : -1;
- if (col == 2)
- {
- var slot = btn != null ? Grid.GetRow(btn) : -1;
- _offCommands[slot].Exceute();
- _unDoCommand = _offCommands[slot];
- }
- }
Client
The client is the one that associates a concrete command with its concrete receiver and also assigns each slot in the remote a command.
- MainWindow remotecontrol = new MainWindow();
-
- Waschmachine waschmachine= new Waschmachine();
-
- Oven oven= new Oven();
-
- SwitchOffOven switchOffOven= new SwitchOffOven(oven);
- SwitchOnOven switchOnOven= new SwitchOnOven(oven);
-
-
- SwitchOnWaschmachine switchOnWaschmachine= new SwitchOnWaschmachine(waschmachine);
- SwitchOffWaschmachine switchOffWaschmachine= new SwitchOffWaschmachine(waschmachine);
-
- remotecontrol.SetCommand(0, switchOnOven, switchOffOven);
- remotecontrol.SetCommand(1, switchOnWaschmachine, switchOffWaschmachine);
Macro Commands
Many of us have certainly used macros in Excel. Macros are quite useful when you want to execute a series of commands every now and then. The only point to remember is that this macro (series of commands) must be stored somewhere.
In our example too, a remote control with macro commands can play a vital role to attract customers. For example, let’s say we need a button/mode on our remote. With this button, the user can configure much of the equipment all together. Let’s call this mode, the party mode. When party mode is selected, the remote should dim the lights, play the CD player, set the volume and so on. Fundamentally, the remote must know which commands to executes and in which order. That’s where Macro Commands are relevant.
Let’s see an example as in the following:
-
- Light light= new Light();
- TV tv= new TV();
- --------
-
- LightOnCommand lightOn= new LightOnCommand(light);
- TVOnCommand TVOn= new TVOnCommand(Tv);
- ------
-
- Command[] partyOn={lightOn, TVOn,…..};
- Command[] partyOff={lightOff,TV TVOff,…..};
-
- MacroCommand partyOnMacro= new MacroCommand(partyOn);
- MacroCommand partyOffMacro= new MacroCommand(partyOff);
-
- remoteControl.SetCommand(7, partyOnMacro,partyOffMacro);
- -------
-
- remoteControl.SwitchOn();
- -----
-
- remoteControl.SwitchOff();
Macro Commands
- Can make a Concrete Command (subclass of Command) that contains a list of commands.
- Commands can be given at construction, or set up via method calls.
- The Execute method calls the execute method on each of the commands in the list.
- How can we use this to provide a good progress indicator?
Other usages of command
- Queuing requests
– Can allocate commands to various threads for processing to load balance between threads/processors.
- Logging requests (audit trail)
– Just needs to save the command objects as they execute.
– If something goes wrong, we can read the log and re-create the sequence of commands (so no data is lost).
– Can also back out changes that cause trouble
Advantages and Disadvantages
- Creating a structure
– Invoker and Receiver are independent of each other.
- Extensibility
– add a new command without changing the existing code.
- A sequence of commands
– Macro Commands