The Command Pattern
CSCI 3132 Summer 2011
Example:
Remote
Control
Given
remote
control
with
seven
programmable
slots.
A
dierent
device
can
be
put
into
each
slot.
There
is
an
On
and
O
switch
for
each
device
slot.
Global
Undo
buBon
undoes
the
last
buBon
pressed.
Also
given
a
CD
with
dierent
vendor
classes
that
have
already
been
wriBen
(for
the
dierent
devices,
TV,
Light,
Sprinkler,
etc)
First
Thoughts
We
know
that
there
are
seven
programmable
slots
for
dierent
devicesso
each
device
may
possibly
adhere
to
some
common
interface.
We
know
that
we
need
to
turn
each
device
on
or
o..so
that
needs
to
be
commonly
done
for
any
device.
Undo
needs
to
work
for
any
device
as
well.
What
Varies?
What
stays
the
same?
What
Varies
The
actual
device
assigned
to
a
slot
The
instrucRon
for
On
on
a
specic
device
The
instrucRon
for
O
on
a
specic
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
O
Capability
to
undo
the
last
acRon
requested
against
the
device
4
The
Vendor
Classes
Vendor
classes
have
been
provided
to
us
via
a
CD.
Ceiling
Light
TV
HoBub
We
know
that
each
device
needs
an
On
or
O
state.
Since
the
capability
to
turn
a
device
On
or
O
is
something
that
stays
the
same.
However,
each
vendor
class
has
their
own
unique
way
of
doing
On
or
O.
5
One
possible
soluRon
Problems:
if (slot1 == Light) light.on(); Else if (slot1 == Hottub) { hottub.prepareJets(); hottub.jetsOn(); } Else if (slot1 == TV) tv.on(); //
The Remote needs to be aware of all the details about turning a device on (or off). If device On/Off mechanism changes, the Remote code will need to be changed. If a new device is added, this code would need to be changed.
Alsowhat about undo????
Is this Open for Extension??
SeparaRon
of
Concerns
The
Vendor
Class
One
(or
more)
methods
that
dene
On
One
(or
more)
methods
that
dene
O
The
Command
Sends
a
message
to
a
device
(On
or
O)
Handle
the
undo
of
a
message
Possible
future
enhancements
Logging
request
Queue
request
The
Remote
handles
one
or
more
Commands.
The
Remote
doesnt
know
anything
about
the
actual
vendor
class
specics.
7
The
Command
PaBern
Allows
you
to
decouple
the
requestor
of
the
acRon
from
the
object
that
performs
the
acRon.
A
Command
object
encapsulates
a
request
to
do
something.
Note:
A
Command
object
handles
a
single
request.
The
Command
interface
(in
this
example)
just
does
one
thing
executes
a
command.
8
LightOnCommand
A
command
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 vendor class.
Constructor takes the vendor class as parameter.
Here the command delegates execution to the vendor class. Could be more steps. 9
SimpleRemoteControl
An
invoker
public class SimpleRemoteControl { Command slot; public SimpleRemoteControl() {} public void setCommand(Command command) { setCommand assigns slot = command; a Command to a slot. } public void buttonWasPressed() { slot.execute(); } } Tells the command to execute. Note that any command would work here. The remote doesnt know anything about the specific vendor class. 10 This version of the remote just has one slot.
EncapsulaRon
An encapsulated Request
action() execute()
execute()
execute() { receiver.action () }
Invoker
execute()
Command encapsulates a Receiver object, use delegation to the corresponding device, Different commands can fit into a Remote Slot in the remote control.
11
RemoteControlTest
A
client
SimpleRemoteControl remote = new SimpleRemoteControl(); Light light = new Light(); GarageDoor garageDoor = new GarageDoor();
Create two vendor classes.
LightOnCommand lightOn = Create two commands new LightOnCommand(light); based on these vendor GarageDoorOpenCommand garageOpen = classes. new GarageDoorOpenCommand(garageDoor); remote.setCommand(lightOn); remote.buttonWasPressed(); remote.setCommand(garageOpen); remote.buttonWasPressed();
Set the command and press button 12
The
Command
PaBern
GoF
Intent:
Encapsulates
a
request
as
an
object,
thereby
le\ng
you
parameterize
other
objects
with
dierent
requests,
queue
or
log
requests,
and
support
undoable
operaRons.
Par(cipants:
Client
(RemoteControlTest)
creates
command
and
associates
command
with
receiver.
Receiver
(TV,
HotTub,
ec)
knows
how
to
perform
the
work.
Concrete
Command
(LightOnCommand)
-
implementaRon
of
Command
interface
Command
Interface
denes
interface
for
all
commands.
Invoker
(Remote
Control)
holds
reference
to
a
command
and
calls
execute()
method
13
Command
PaBern
Class
Diagram
Client Invoker setCommand() <<Interface>> Command execute() undo()
Receiver action()
ConcreteComman d execute() undo()
Client
creates
a
ConcreteCommand
and
binds
it
with
a
Receiver.
Client
hands
the
ConcreteCommand
over
to
the
Invoker
which
stores
it.
14
The
Sequence
Diagram
15
Class
Diagram
for
Home
automaRon
Creates command objects, binds with devices RemoteLoader Invokes execute() method of the button command object RemoteControl onCommands offCommands setCommand() onButtonPushed() offButtonPushed() <<Interface>> Command execute() undo()
Light on() off()
LightOnCommand execute() LightOffCommand undo() execute() undo()
16
Macro
Commands
Party
mode
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
Adding
Undo
Easy
for
some
objects
public class LightOnCommand implements Command { Light light; public LightOnCOmmand(Light light) { this.light = light; } public void execute() { light.on(); } public void undo() { light.off(); } }
18
Adding
Undo
ImplemenRng
the
remote
control
with
undo.
public class Command[] Command[] Command[] RemoteControl { on Commands; offCommands; 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, Command offCommand) { onCommand[slot] = onCommand; offCommand[slot] = offCommand; }
19
Adding
Undo
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(); } // }
20
Adding
Undo
For
Ceiling
Fan
public class CeilingFan { public static final int public static final int public static final int public static final int HIGH = 3; MEDIUM = 2; LOW= 1; 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 }
21
Adding
Undo
For
Ceiling
Fan
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();}}
22
History
of
Undo
OperaRons
If
the
Undo
buBon
is
pressed
mulRple
Rmes,
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,
LightOCommand,
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?
23
History
of
Undo
OperaRons
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(); }
24
Simple
Logging
We
want
to
enhance
the
Remote
Control
again
to
log
every
Rme
a
Command
is
executed
(on
or
o).
Which
object
should
we
enhance??
25
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!
26
Complex
Logging
Lets
say
we
had
a
spreadsheet
applicaRon
and
we
know
it
may
crash
oaen.
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.
27
Complex
Logging
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() }
Once again, we can make these changes in one place (the Invoker)
28
Summary
so
far..
OO
Basics
AbstracRon
EncapsulaRon
Inheritance
Polymorphism
Encapsulate
what
varies
Favor
composiRon
over
inheritance
Program
to
interfaces
not
to
implementaRons
Strive
for
loosely
coupled
designs
between
objects
that
interact
Classes
should
be
open
for
extension
but
closed
for
modicaRon.
Depend
on
abstracts.
Do
not
depend
on
concrete
classes.
29
OO
Principles