0% found this document useful (0 votes)
15 views61 pages

Command

The document discusses the command design pattern, including examples of using it to control devices from a remote control. It describes the key components of the pattern including commands, receivers, and invokers. It provides examples of implementing on and off commands for different devices. It also includes labs asking the reader to implement additional commands to control multiple devices or improve code reuse.

Uploaded by

Keshara Kavinda
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
15 views61 pages

Command

The document discusses the command design pattern, including examples of using it to control devices from a remote control. It describes the key components of the pattern including commands, receivers, and invokers. It provides examples of implementing on and off commands for different devices. It also includes labs asking the reader to implement additional commands to control multiple devices or improve code reuse.

Uploaded by

Keshara Kavinda
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
You are on page 1/ 61

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

You might also like