designpatterns-09.pdf
TRANSCRIPT
-
7/29/2019 designPatterns-09.pdf
1/29
The Command Pattern
CSCI 3132 Summer 20111
-
7/29/2019 designPatterns-09.pdf
2/29
Example:RemoteControl
Givenremotecontrolwithsevenprogrammableslots.
Adifferentdevicecanbeputintoeachslot. ThereisanOnandOffswitchforeachdeviceslot. GlobalUndobuBonundoesthelastbuBonpressed. AlsogivenaCDwithdifferentvendorclassesthat
havealreadybeenwriBen(forthedifferentdevices,
TV,Light,Sprinkler,etc)
2
-
7/29/2019 designPatterns-09.pdf
3/29
FirstThoughts
Weknowthattherearesevenprogrammableslotsfordifferentdevicessoeachdevicemaypossibly
adheretosomecommoninterface.
Weknowthatweneedtoturneachdeviceonoroff..sothatneedstobecommonlydoneforany
device.
Undoneedstoworkforanydeviceaswell.
3
-
7/29/2019 designPatterns-09.pdf
4/29
WhatVaries?Whatstaysthesame?
WhatVariesTheactualdeviceassignedtoaslotTheinstruconforOnonaspecificdeviceTheinstruconforOffonaspecificdevice
WhatStaystheSameDevicewithsevenslotsCapabilitytoassignaslottoadeviceCapabilitytorequestthatadeviceturnOnorOffCapabilitytoundothelastaconrequestedagainst
thedevice
4
-
7/29/2019 designPatterns-09.pdf
5/29
TheVendorClasses
VendorclasseshavebeenprovidedtousviaaCD.CeilingLightTV
HoBub
WeknowthateachdeviceneedsanOnorOffstate.
SincethecapabilitytoturnadeviceOnorOffissomethingthatstaysthesame.
However,eachvendorclasshastheirownuniquewayofdoingOnorOff.
5
-
7/29/2019 designPatterns-09.pdf
6/29
Onepossiblesoluon
if (slot1 == Light)
light.on();
Else if (slot1 == Hottub) {
hottub.prepareJets();
hottub.jetsOn();
} Else if (slot1 == TV)
tv.on();
//
Problems:
The Remote needs to beaware of all the details aboutturning a device on (or off).
If device On/Off mechanism
changes, the Remote codewill need to be changed.
If a new device is added, this
code would need to bechanged.
Is this Open forExtension??
Alsowhat aboutundo????
6
-
7/29/2019 designPatterns-09.pdf
7/29
SeparaonofConcerns
TheVendorClass One(ormore)methodsthatdefineOn One(ormore)methodsthatdefineOff
TheCommand Sendsamessagetoadevice(OnorOff) Handletheundoofamessage Possiblefutureenhancements
LoggingrequestQueuerequest
TheRemotehandlesoneormoreCommands.TheRemotedoesntknowanythingabouttheactual
vendorclassspecifics.7
-
7/29/2019 designPatterns-09.pdf
8/29
TheCommandPaBern
Allowsyoutodecoupletherequestoroftheaconfromtheobjectthatperformstheacon.
ACommandobjectencapsulatesarequesttodosomething.
Note:ACommandobjecthandlesasinglerequest. TheCommandinterface(inthisexample)justdoes
onethingexecutesacommand.
8
-
7/29/2019 designPatterns-09.pdf
9/29
LightOnCommandAcommand
public class LightOnCommand implements Command {
Light light;
public LightOnCommand(Light light) {
this.light = light;
}
public void execute() {
light.on();}
}
The command is
composed of a vendorclass.
Constructor takes the
vendor class asparameter.
Here the command delegates
execution to the vendor class.Could be more steps.
9
-
7/29/2019 designPatterns-09.pdf
10/29
SimpleRemoteControlAninvoker
public class SimpleRemoteControl {
Command slot;
public SimpleRemoteControl() {}
public void setCommand(Command command) {
slot = command;
}
public void buttonWasPressed() {
slot.execute();
}
}
This version of the
remote just has oneslot.
setCommand assigns
a Command to a slot.
Tells the command to
execute. Note that anycommand would work
here. The remotedoesnt know anything
about the specific
vendor class.10
-
7/29/2019 designPatterns-09.pdf
11/29
Encapsulaon
action()
execute()
{ receiver.action
()
}
execute()
execute()
execute()
An encapsulated Request
Invoker
Command encapsulates a Receiver object, use delegation to thecorresponding device,Different commands can fit into a Remote Slot in the remote control.
11
-
7/29/2019 designPatterns-09.pdf
12/29
RemoteControlTestAclient
SimpleRemoteControl remote = new SimpleRemoteControl();
Light light = new Light();
GarageDoor garageDoor = new GarageDoor();
LightOnCommand lightOn =
new LightOnCommand(light);
GarageDoorOpenCommand garageOpen =
new GarageDoorOpenCommand(garageDoor);
remote.setCommand(lightOn);remote.buttonWasPressed();
remote.setCommand(garageOpen);
remote.buttonWasPressed();
Create two vendor
classes.
Create two commandsbased on these vendorclasses.
Set the command and press
button
12
-
7/29/2019 designPatterns-09.pdf
13/29
TheCommandPaBern
GoFIntent:Encapsulatesarequestasanobject,therebyle\ngyouparameterizeotherobjectswithdifferent
requests,queueorlogrequests,andsupportundoable
operaons.
Par(cipants: Client(RemoteControlTest)createscommandandassociatescommand
withreceiver.
Receiver(TV,HotTub,ec)knowshowtoperformthework. ConcreteCommand(LightOnCommand)-implementaonofCommand
interface
CommandInterfacedefinesinterfaceforallcommands. Invoker(RemoteControl)holdsreferencetoacommandandcalls
execute()method
13
-
7/29/2019 designPatterns-09.pdf
14/29
CommandPaBernClassDiagram
Client Invoker
setCommand()
Command
execute()undo()
Receiver
action()
ConcreteCommand
execute()undo()
ClientcreatesaConcreteCommandandbindsitwithaReceiver. ClienthandstheConcreteCommandovertotheInvokerwhichstores
it.14
-
7/29/2019 designPatterns-09.pdf
15/29
TheSequenceDiagram
15
-
7/29/2019 designPatterns-09.pdf
16/29
ClassDiagramforHomeautomaon
RemoteLoader
RemoteControl
onCommandsoffCommandssetCommand()onButtonPushed()offButtonPushed()
Command
execute()undo()
Light
on()
off()
LightOnCommand
execute()undo()LightOffCommand
execute()undo()
Creates command objects,binds with devices
Invokes execute() method ofthe button command object
16
-
7/29/2019 designPatterns-09.pdf
17/29
MacroCommandsPartymode
public class MacroCommand implements Command {
Command[] commands;
public MacroCommand(Command[] commands) {
this.commands = commands;
}
public void execute() {
for (int i = 0; i < commands.length; i++) {
commands[i].execute();
}
}
public void undo() {for (int i = 0; i < commands.length; i++) {
commands[i].undo();
}
}
}
17
-
7/29/2019 designPatterns-09.pdf
18/29
Easyforsomeobjects
public class LightOnCommand implements Command {
Light light;
public LightOnCOmmand(Light light) {
this.light = light;
}
public void execute() {
light.on();
}
public void undo() {
light.off();}
}
AddingUndo
18
-
7/29/2019 designPatterns-09.pdf
19/29
Implemenngtheremotecontrolwithundo.public class RemoteControl {
Command[] on Commands;
Command[] offCommands;
Command[] undoCommands;
public RemoteControl() {onCommands = new Command[7];
offCommands = new Command[7];
Command noCommand = new NoCommand();
for (int i = 0; i < 7; i++) {
onCommands[i] = noCommand;
offCommands[i] = noCommand;}
undoCommand = noCommand;
}
public void setCommand(int slot, Command onCommand, CommandoffCommand) {
onCommand[slot] = onCommand;
offCommand[slot] = offCommand;
}
AddingUndo
19
-
7/29/2019 designPatterns-09.pdf
20/29
public void onButtonWasPushed(int slot) {
onCommands(slot].execute();
undoCommand = onCommands[slot];
}
public void offButtonWasPushed(int slot) {offCommands(slot].execute();
undoCommand = offCommands[slot];
}
public void undoButtonwWasPushed() {
undoCommand.undo();
}
//
}
AddingUndo
20
-
7/29/2019 designPatterns-09.pdf
21/29
public class CeilingFan {
public static final int HIGH = 3;public static final int MEDIUM = 2;
public static final int LOW= 1;
public static final int OFF= 0;
public CeilingFan(String location) {
this.location = location;
speed = OFF;}
public void high() {
speed = HIGH;
//code to set fan to high
}
public void medium() {
speed = MEDIUM;//code to set fan to medium
}
//
public void off() {
speed = OFF;
//code to turn fan off
}
AddingUndoForCeilingFan
21
-
7/29/2019 designPatterns-09.pdf
22/29
public class CeilingFanHighCommand implements Command {CeilingFan ceilingFan;
int prevSpeed;
public CeilingFanHighCommand(CeilingFan ceilingFan) {
this.ceilingFan = ceilingFan;
}
public void execute() {prevSpeed = ceilingFan.getSpeed();
ceilingFan.high();
}
public void undo() {
if (prevSpeed == CeilingFan.HIGH) {
celingFan.high();
} else if (prevSpeed = CeilingFan.MEDIUM) {
ceilingFan.medium();
} else if (prevSpeed = CeilingFan.LOW) {
ceilingFan.low();
} else if (prevSpeed = CeilingFan.OFF) {
ceilingFan.off();}}
AddingUndoForCeilingFan
22
-
7/29/2019 designPatterns-09.pdf
23/29
HistoryofUndoOperaons
IftheUndobuBonispressedmulplemes,wewanttoundoeachcommandthathadbeenpreviouslyapplied.
Whichobjectshouldweenhancetostoreahistoryofeachcommandapplied?Why?
Client(RemoteControlTest) Receiver(TV,DVD,etc) ConcreteCommand(LightOnCommand,LightOffCommand,
etc)
Invoker(RemoteControl) Weneedtobeabletoaddcommandstoalistandthenlater
getthemostrecentone.Whatkindofobjectcanweuse?
23
-
7/29/2019 designPatterns-09.pdf
24/29
HistoryofUndoOperaonspublic class RemoteControl {
Stack 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();
} 24
-
7/29/2019 designPatterns-09.pdf
25/29
SimpleLogging
WewanttoenhancetheRemoteControlagaintologeverymeaCommandisexecuted(onoroff).
Whichobjectshouldweenhance??
25
-
7/29/2019 designPatterns-09.pdf
26/29
SimpleLogging
ChangestoRemoteControl 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. Nochange is needed in any of the Command or Receiverobjects! 26
-
7/29/2019 designPatterns-09.pdf
27/29
ComplexLogging
Letssaywehadaspreadsheetapplicaonandweknowit
maycrashoen.
Forfailurerecovery,wecouldperiodicallystoreabackupof
thespreadsheetevery5minutes...orwecould
periodicallypersistthelistofcommandsexecutedonthespreadsheetsincethelastsavepoint.
Whenacrashoccurs,weloadthelastsaveddocumentand
re-applythepersistedcommands.
27
-
7/29/2019 designPatterns-09.pdf
28/29
public void onButtonWasPushed(int slot) {onCommands[slot].execute();
StoreToDisk(onCommands[slot]);
}
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()
}
ComplexLogging
Once again,we can makethese changesin one place(the Invoker)
28
-
7/29/2019 designPatterns-09.pdf
29/29
Summarysofar..
OOBasics Abstracon Encapsulaon Inheritance Polymorphism
OOPrinciples Encapsulatewhatvaries Favorcomposionoverinheritance Programtointerfacesnottoimplementaons
Striveforlooselycoupleddesignsbetweenobjectsthatinteract
Classesshouldbeopenforextensionbutclosedformodificaon.
Dependonabstracts.Donotdependonconcreteclasses.29