Design Patterns
Command
05/09/2024 1
Agenda
Command Pattern
Head First Example (Remote Control)
Lab
History of Undo Operations
Simple Logging
Complex Logging
Case Study: Command Management
Distributed Command Pattern
05/09/2024 2
Remote Control
Given remote control with seven programmable slots.
A different device can be put into each slot.
There is an On and Off switch for each device slot.
Global Undo button undoes the last button pressed.
Also given a CD with different vendor classes that have
already been written (for the different devices, TV, Light,
Sprinkler, etc)
05/09/2024 3
First Thoughts
We know that there are seven programmable slots for
different devices…so each device may possibly adhere
to some common interface.
We know that we need to turn each device “on” or
“off”..so that needs to be commonly done for any device.
Undo needs to work for any device as well.
05/09/2024 4
What Varies? What stays the same?
What Varies
The actual device assigned to a slot
The instruction for “On” on a specific device
The instruction for “Off” on a specific device
What Stays the Same
Device with seven slots
Capability to assign a slot to a device
Capability to request that a device turn On or Off
Capability to undo the last action requested against the device
05/09/2024 5
The Vendor Classes (pg 194)
Vendor classes have been provided to us via a CD.
Ceiling Light
TV
Hottub
We know that each device needs an “On” or “Off” state.
Since the capability to turn a device “On” or “Off” is something
that “stays the same”.
However, each vendor class has their own unique way of doing
“On” or “Off”.
05/09/2024 6
One possible solution…
if (slot1 == Light) Problems:
light.on();
The Remote needs to be
Else if (slot1 == Hottub) { aware of all the details about
hottub.prepareJets(); turning a device on (or off).
hottub.jetsOn();
If device On/Off mechanism
changes, the Remote code
} Else if (slot1 == TV) will need to be changed.
tv.on();
If a new device is added, this
etc code would need to be
changed.
Is this Open for Extension??
05/09/2024 Also…what about undo???? 7
Separation of Concerns
The Vendor Class
One (or more) methods that define “On”
One (or more) methods that define “Off”
The Command
Sends a message to a device (On or Off)
Handle the undo of a message
Possible future enhancements
Logging request
Queue request
The Remote – handles one or more Commands. The
Remote doesn’t know anything about the actual vendor
class specifics.
05/09/2024 8
The Command Pattern
“Allows you to decouple the requestor of the action from
the object that performs the action.”
“A Command object encapsulates a request to do
something.”
Note: A Command object handles a single request.
05/09/2024 9
Command Interface (pg203)
public interface Command {
public void execute();
}
The Command interface (in this example) just does one
thing..executes a command.
10
05/09/2024
LightOnCommand (pg203)
public class LightOnCommand implements Command {
The command is
Light light; composed of a vendor
class.
public LightOnCommand(Light light) {
this.light = light;
} Constructor takes the
vendor class as
parameter.
public void execute() {
light.on();
}
}
Here the command delegates
execution to the vendor class.
Note: This is a simple
example..there could be more
steps.
05/09/2024 11
SimpleRemoteControl (pg204)
This version of the
public class SimpleRemoteControl { remote just has one
Command slot; slot.
public SimpleRemoteControl() {}
public void setCommand(Command command) {
slot = command; setCommand
} assigns a Command
to a slot.
public void buttonWasPressed() {
slot.execute(); Tells the command to
} execute. Note that any
} command would work
here. The remote
doesn’t know anything
about the specific
vendor class.
05/09/2024 12
RemoteControlTest (pg204)
SimpleRemoteControl remote = new
SimpleRemoteControl();
Create two vendor
Light light = new Light(); classes.
GarageDoor garageDoor = new GarageDoor();
LightOnCommand lightOn = Create two commands
new LightOnCommand(light); based on these vendor
classes.
GarageDoorOpenCommand garageOpen =
new GarageDoorOpenCommand(garageDoor);
remote.setCommand(lightOn);
Set the command and press
remote.buttonWasPressed();
button
remote.setCommand(garageOpen);
remote.buttonWasPressed();
05/09/2024 13
The Command Pattern
GoF Intent: “Encapsulates a request as an object,
thereby letting you parameterize other objects with
different requests, queue or log requests, and support
undoable operations.”
See diagrams on pg 206
Command encapsulates a Receiver object
Different commands can fit into a Remote Slot (which exists in
the Remote Control)
05/09/2024 14
05/09/2024 15
Definitions (see Diagram on pg 207)
Client (RemoteControlTest) – creates command and
associates command with receiver.
Receiver (TV, HotTub, ec)– knows how to perform the
work.
Concrete Command (LightOnCommand) -
implementation of Command interface
Command Interface – defines interface for all
commands.
Invoker (Remote Control) – holds reference to a
command and calls execute() method against it. 16
Lab Part I
We get a new vendor class for DVD. To turn on the
DVD, call the function TurnPowerOn(). To turn off, you
call TurnPowerOff().
Create an On and Off command object for the DVD
player.
05/09/2024 17
Lab Part II
The customer wants to be able to turn the DVD and TV
on (and off) at the same time.
Create a command that will set up the DVD and TV with
one button click.
05/09/2024 18
Lab Part III (Design Challenge!)
Look at the LightOnCommand and LightOffCommand on
pg 217.
Note that there is a duplication of code between these two
objects.
Can you create an abstract class called Command that
can help remove this duplication of code…and yet support
Undo??
05/09/2024 19
Lab Part I Answer
public class DVDOnCommand : Command {
DVD dvd;
public DVDOnCommand(DVD d)
{
this.dvd = d;
}
public void execute()
{
dvd.TurnPowerOn();
}
}
05/09/2024 20
Lab Part I Answer (cont)
public class DVDOffCommand : Command {
DVD dvd;
public DVDOffCommand(DVD d)
{
this.dvd = d;
}
public void execute()
{
dvd.TurnPowerOff();
}
}
05/09/2024 21
Lab Part II Answer
public class DVDTvOnCommand : Command {
DVD dvd;
TV tv;
public DVDTvOnCommand(DVD d, TV t)
{
this.dvd = d;
this.tv = t;
}
public void execute()
{
tv.on();
tv.changeInputToDvd();
dvd.TurnPowerOn();
}
}
05/09/2024 22
Lab Part III (Answer)
public enum CommandState
{
NOTSET,
ON,
OFF
}
05/09/2024 23
Lab Part III (Answer)
public abstract class CommandBase
{
CommandState state;
public CommandBase()
{
state = CommandState.NOTSET;
}
abstract protected void executeOn();
abstract protected void executeOff();
05/09/2024 24
public void on() {
state = CommandState.ON;
We don’t want on(), off(), and
executeOn();
undo() to be overridden.
}
public void off() {
state = CommandState.OFF;
executeOff();
}
public void undo() {
if (state == CommandState.ON)
off();
else if (state == CommandState.OFF)
on();
else if (state == CommandState.NOTSET)
{
//do nothing
}
}
25
Lab Part III
public class LightCommand : CommandBase
{
Light light;
public LightCommand(Light l) {
this.light = l;
}
protected override void executeOn()
{
light.on();
}
protected override void executeOff()
{
light.off();
}
}
05/09/2024 26
Lab Part III
The client that calls this code…
CommandBase c = new LightCommand(new Light());
c.on();
c.undo();
05/09/2024 27
History of Undo Operations
If the Undo button is pressed multiple times, we want to
undo each command that had been previously applied.
Which object should we enhance to store a history of
each command applied? Why?
Client (RemoteControlTest)
Receiver (TV, DVD, etc)
ConcreteCommand (LightOnCommand, LightOffCommand, etc)
Invoker (RemoteControl)
We need to be able to add commands to a list and then
later get the most recent one. What kind of object can
we use?
05/09/2024 28
History of Undo Operations
public class RemoteControl {
Stack<Command> undoStack; //this gets initialized in
//constructor.
public void onButtonWasPushed(int slot) {
onCommands[slot].execute();
undoStack.push(onCommands[slot]);
}
public void offButtonWasPushed(int slot) {
offCommands[slot].execute();
undoStack.push(offCommands[slot]);
}
public void undoButtonWasPushed() {
Command c = undoStack.pop();
c.undo();
}
29
Simple Logging
We want to enhance the Remote Control again to log
every time a Command is executed (on or off).
Which object should we enhance??
05/09/2024 30
Simple Logging
Changes to RemoteControl…
public void onButtonWasPushed(int slot) {
onCommands[slot].execute();
//Log here
}
public void offButtonWasPushed(int slot) {
offCommands[slot].execute();
//Log here
}
Advantage: We can add logging in the Invoker. No change is needed in any
of the Command or Receiver objects!
05/09/2024 31
Complex Logging
Let’s say we had a spreadsheet application and we know it
may crash often.
For failure recovery, we could periodically store a backup of
the spreadsheet every 5 minutes...or we could
periodically persist the list of commands executed on the
spreadsheet since the last save point.
When a crash occurs, we load the last saved document
and re-apply the persisted commands.
05/09/2024 32
Complex Logging
public void onButtonWasPushed(int slot) { Once again, we can
onCommands[slot].execute(); make these
StoreToDisk(onCommands[slot]); changes in one
} place (the Invoker)
public void offButtonWasPushed(int slot) {
offCommands[slot].execute();
StoreToDisk(offCommands[slot]);
}
public void SaveButtonPressed() {
//Delete stored commands from disk.
}
public void RestoreCommands() {
//load last saved state
Commands[] storedCommands = GetCommandsFromDisk();
//for each Command, call execute()
}
05/09/2024 33
Case Study: Command Management
The File menu contains the
items:
• Open
• Save
• Print
Also menu item “Edit
Copy “ which is enabled
when text is selected.
Same toolbar buttons for
each menu item.
05/09/2024 34
Question
Imagine that there is a Command for each action
Open
Save
Print
Copy
How would you associate each Menu/Toolbar pair with a
Command?
05/09/2024 35
Possible Solutions
In both the Menu and Toolbar click events, write all of the
code for performing the command.
(or) Consolidate all of the logic in one place and have the
Menu and Toolbar events call the same logic.
What are the disadvantages of this approach?
05/09/2024 36
Disadvantages
The developer would need to enforce that each UI
element calls the right command.
Managing state can be become an issue.
For example, input form with three different UI elements for
Save. (Menu Item, Toolbar Button, Button next to the Form)
When the form is in Edit mode all of these elements should be
Enabled. When the form is not in Edit mode, they need to be
disabled.
saveMenuItem.Enabled = false;
saveButton.Enabled = false;
saveToolbar.Enabled = false
05/09/2024 37
Command Management Framework
Based on the Command Pattern
Framework for associating multiple UI Elements to the
same Command
Can associate UI Elements and Commands in one
place.
You can send a message to a Command
“Tell all of your UI Elements to Turn On or Off”
05/09/2024 38
Example
// Create Command Manager object
cmdMgr = new CommandManager();
//Create a Command “Edit Copy” with a Execute and Copy
//functionality.
cmdMgr.Commands.Add( new Command(
"EditCopy",
new Command.ExecuteHandler(OnCopy),
new Command.UpdateHandler(UpdateCopyCommand)));
//Associate Command “Edit Copy” with different UI elements (Menu and
Toolbar)
cmdMgr.Commands["EditCopy"].CommandInstances.Add(
new Object[]{mnuEditCopy, tlbMain.Buttons[4]});
05/09/2024 39
Command Manager
The Commands property
contains a list of all
possible Commands.
CommandsList contains
a List object internally for
storing each possible
Command. It also has a
reference to
CommandManager.
05/09/2024 40
Command Object
ExecuteHandler = delegate that represents the logic for executing the
actual Command logic. Triggered when the command is executed. Gets
associated with OnExecute event.
UpdateHandler = delegate that represents the logic for executing the
logic to update the state of a command. (For example, Edit Copy
should be enabled if text has been selected). Associated with OnUpdate
event. 41
Command Constructor
public Command( string strTag,
ExecuteHandler handlerExecute,
UpdateHandler handlerUpdate)
{
this.strTag = strTag;
Associates events
OnExecute += handlerExecute; to delegates
OnUpdate += handlerUpdate;
}
//Delegates
public delegate void ExecuteHandler(Command cmd);
public delegate void UpdateHandler(Command cmd);
// Events
public event ExecuteHandler OnExecute;
public event UpdateHandler OnUpdate;
05/09/2024 42
Command: Execute() and ProcessUpdates()
// Methods to trigger events
public void Execute()
{
Will call the function
if (OnExecute != null)
passed in as
OnExecute(this);
ExecuteHandler
}
internal void ProcessUpdates()
{ Will call the function
if (OnUpdate != null) passed in as
OnUpdate(this); UpdateHandler
}
How is this Command.Execute() different than the RemoteControl example??
43
Re-look at EditCopy
cmdMgr.Commands.Add( new Command(
"EditCopy",
new Command.ExecuteHandler(OnCopy),
new Command.UpdateHandler(UpdateCopyCommand)));
//Here is the logic of the actual command
public void OnCopy(Command cmd) {
Clipboard.SetDataObject(txtEditor.SelectedText);
}
//Defines the condition for when the command should be “on”
public void UpdateCopyCommand(Command cmd) {
cmd.Enabled = txtEditor.SelectedText.Length > 0;
}
Will discuss later
05/09/2024 44
So far..
Command #1
Tag = “Edit Copy”
OnExecute = OnCopy
OnUpdate = UpdateCopyCommand
Command #2
Tag = “File Open”
OnExecute = OnFileOpen
OnUpdate = null
05/09/2024 45
Associating UI Elements to a Command
//Associate Command “Edit Copy” with different UI elements
//(Menu and Toolbar)
cmdMgr.Commands["EditCopy"].CommandInstances.Add(
new Object[]{mnuEditCopy, tlbMain.Buttons[4]});
What object contains the CommandInstances property??
05/09/2024 46
CommandInstances
Contain a list of all UI Elements
associated with a Command
05/09/2024 47
So far..
Two items:
mnuEditCopy
tlbMain.Buttons[4]
Command #1
Tag = “Edit Copy”
OnExecute = OnCopy
OnUpdate = UpdateCopyCommand
All we have done so far
is store information…
05/09/2024 48
How do we enable a Command???
In CommandManager, there is an event handler for the
Application Idle Event:
private void OnIdle(object sender, System.EventArgs args)
{
IDictionaryEnumerator myEnumerator =
(IDictionaryEnumerator)Commands.GetEnumerator();
while ( myEnumerator.MoveNext() )
{
Command cmd = myEnumerator.Value as Command;
if (cmd != null)
cmd.ProcessUpdates();
Are you enabled???
}
}
49
Command.ProcessUpdates()
internal void ProcessUpdates()
{
if (OnUpdate != null)
OnUpdate(this);
}
For Edit Copy, the following code will be called:
public void UpdateCopyCommand(Command cmd) {
cmd.Enabled = txtEditor.SelectedText.Length > 0;
}
If we do need to enable (or disable) the Command, who do we need to tell
to “go enable/disable yourself?”
05/09/2024 50
Command.Enabled Property
The psuedo-code for this property is the following:
Enabled = true or false //we got this information
externally
foreach (object in CommandInstances)
//enable or disable yourself.
End for
So if there is a Menu Item, ToolBar, etc, we want to enable
each one. But note that CommandInstances is just a
collection of generic objects…
05/09/2024 51
Command.Enabled (Psuedo-Code)
foreach (object in CommandInstances)
if (object is MenuItem) {
MenuItem m = (MenuItem)object;
m.Enabled = (true or false);
} else if (object is Toolbar) {
Toolbar t = (Toolbar)object;
t.Enabled = (true or false);
} else {…}
End for
Question: Any problems with this code???
05/09/2024 52
Command.Enabled (Psuedo-Code)
foreach (object in CommandInstances)
commandExecutor =
GetCommandExecutor(typeof(object);
commandExecutor.Enable( object, true/false);
End for
05/09/2024 53
CommandExecutor
Enable() is abstract. Toolbar and Menu subclasses must
define how Enable needs to work.
05/09/2024 54
CommandExecutor
public abstract class CommandExecutor
{
public abstract void Enable(object item, bool bEnable);
}
public class MenuCommandExecutor : CommandExecutor
{
public override void Enable(object item, bool bEnable)
{
MenuItem mi = (MenuItem)item;
mi.Enabled = bEnable;
}
}
05/09/2024 55
Command.Enabled (Real Code)
public bool Enabled
{
get
{
return enabled;
}
set
{
enabled = value;
foreach(object instance in commandInstances)
{
Manager.GetCommandExecutor(instance).Enable(
instance, enabled);
}
}
}
CommandManager actually contains all possible
CommandExecutors. These are registered during start-up for
each possible type of UI element. (CommandManager
constructor) 56
CommandExecutor - Execution
These objects also help establish the connection between
the UI Event and a Command.
For example, MenuCommandExecutor establishes a link
between the event MenuItem.Click and the associated
Command execution method (command.Execute)
05/09/2024 57
Called when
MenuCommandExecutor we added UI
element to
public class MenuCommandExecutor : CommandExecutor Command
{
public override void InstanceAdded(object item, Command cmd)
{
MenuItem mi = (MenuItem)item;
mi.Click += new System.EventHandler(menuItem_Click);
base.InstanceAdded(item, cmd); //Stores UI/Command
//relationship in
hashtable
}
private void menuItem_Click(object sender, System.EventArgs e)
{
Command cmd = GetCommandForInstance(sender);
cmd.Execute();
}
} 05/09/2024 58
Example (Again)
// Create Command Manager object
cmdMgr = new CommandManager();
//Create a Command “Edit Copy” with a Execute and Copy
//functionality.
cmdMgr.Commands.Add( new Command(
"EditCopy",
new Command.ExecuteHandler(OnCopy),
new Command.UpdateHandler(UpdateCopyCommand)));
//Associate Command “Edit Copy” with different UI elements (Menu and
Toolbar)
cmdMgr.Commands["EditCopy"].CommandInstances.Add(
new Object[]{mnuEditCopy, tlbMain.Buttons[4]});
05/09/2024 59
Confused?
http://msdn.microsoft.com/en-us/magazine/cc188928.aspx
You can download the code!
05/09/2024 60
Distributed Command Pattern
Address Chat Window Problem
http://www.codeproject.com/KB/architecture/
distributedcommandpattern.aspx
05/09/2024 61