You are on page 1of 29

The Command Pattern

CSCI 3132 Summer 2011


Example:  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  buBon  undoes  the  last  buBon  pressed.   •  Also  given  a  CD  with  different  vendor  classes  that   have  already  been  wriBen  (for  the  different  devices,   TV,  Light,  Sprinkler,  etc)  


.   •  We  know  that  we  need  to  turn  each  device  “on”  or   “off”.   3 .First  Thoughts   •  We  know  that  there  are  seven  programmable  slots   for  different  devices…so  each  device  may  possibly   adhere  to  some  common  interface.   •  Undo  needs  to  work  for  any  device  as  that  needs  to  be  commonly  done  for  any   device.

What  Varies?    What  stays  the  same?   •  What  Varies   –  The  actual  device  assigned  to  a  slot   –  The  instrucRon  for  “On”  on  a  specific  device   –  The  instrucRon  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  acRon  requested  against   the  device   4 .

  –  Ceiling  Light     –  TV   –  HoBub   •  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.The  Vendor  Classes   •  Vendor  classes  have  been  provided  to  us  via  a  CD.  each  vendor  class  has  their  own  unique  way   of  doing  “On”  or  “Off”.   5 .

hottub.One  possible  soluRon…   Problems: if (slot1 == Light) light. If device On/Off mechanism changes.on(). If a new device is added.prepareJets(). this code would need to be changed. Else if (slot1 == Hottub) { hottub. the Remote code will need to be changed. //… The Remote needs to be aware of all the details about turning a device on (or off).on(). } Else if (slot1 == TV) tv.jetsOn(). Also…what about undo???? Is this Open for Extension?? 6 .

  7 .SeparaRon  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.

”   – Note:  A  Command  object  handles  a  single  request.   •  The  Command  interface  (in  this  example)  just  does   one  thing…  executes  a  command.   8 .”   •  “A  Command  object  encapsulates  a  request  to  do   something.The  Command  PaBern   •  “Allows  you  to  decouple  the  requestor  of  the  acRon   from  the  object  that  performs  the  acRon.

} } The command is composed of a vendor class. } public void execute() { light.on(). Could be more steps.LightOnCommand  –  A  command   public class LightOnCommand implements Command { Light light. public LightOnCommand(Light light) { this. Constructor takes the vendor class as parameter. Here the command delegates execution to the vendor class.light = light. 9 .

} } Tells the command to execute. 10 This version of the remote just has one slot. The remote doesn’t know anything about the specific vendor class. Note that any command would work here. } public void buttonWasPressed() { slot. . public SimpleRemoteControl() {} public void setCommand(Command command) { setCommand assigns slot = command.SimpleRemoteControl  –  An  invoker   public class SimpleRemoteControl { Command slot.execute(). a Command to a slot.

action () } Invoker execute() Command encapsulates a Receiver object. Different commands can fit into a Remote Slot in the remote control. use delegation to the corresponding device.EncapsulaRon   An encapsulated Request action() execute() execute() execute() { receiver. 11 .

LightOnCommand lightOn = Create two commands new LightOnCommand(light).buttonWasPressed(). remote. Light light = new Light().RemoteControlTest  –  A  client   SimpleRemoteControl remote = new SimpleRemoteControl().setCommand(lightOn). new GarageDoorOpenCommand(garageDoor). remote.setCommand(garageOpen). remote. remote.buttonWasPressed(). GarageDoor garageDoor = new GarageDoor(). Set the command and press button 12 . based on these vendor GarageDoorOpenCommand garageOpen = classes. Create two vendor classes.

 ec)–  knows  how  to  perform  the  work.   –  Receiver  (TV.  thereby   le\ng  you  parameterize  other  objects  with  different   requests.  queue  or  log  requests.  HotTub.  and  support  undoable   operaRons.     –  Invoker  (Remote  Control)  –  holds  reference  to  a  command  and  calls   execute()  method     13 .   –  Concrete  Command  (LightOnCommand)  -­‐  implementaRon  of  Command   interface   –  Command  Interface  –  defines  interface  for  all  commands.”   •  Par(cipants:     –  Client  (RemoteControlTest)  –  creates  command  and  associates  command   with  receiver.The  Command  PaBern   •  GoF  Intent:  “Encapsulates  a  request  as  an  object.

  14 .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.

The  Sequence  Diagram   15 .

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 .Class  Diagram  for  Home  automaRon   Creates command objects.

length.Macro  Commands  –  Party  mode   public class MacroCommand implements Command { Command[] commands.commands = commands. } public void execute() { for (int i = 0. i < commands.undo().execute(). i < commands. i++) { commands[i]. } } } 17 .length. } } public void undo() { for (int i = 0. i++) { commands[i]. public MacroCommand(Command[] commands) { this.

Adding  Undo   Easy  for  some  objects   public class LightOnCommand implements Command { Light light.on(). } public void execute() { light.light = light. public LightOnCOmmand(Light light) { this. } } 18 . } public void undo() {

Command noCommand = new NoCommand(). offCommands[i] = noCommand. Command offCommand) { onCommand[slot] = onCommand. } undoCommand = noCommand. offCommands = new Command[7].     public class Command[] Command[] Command[] RemoteControl { on Commands. public RemoteControl() { onCommands = new Command[7]. for (int i = 0. i++) { onCommands[i] = noCommand. i < 7. undoCommands. } public void setCommand(int slot. } 19 . offCommand[slot] = offCommand.Adding  Undo   ImplemenRng  the  remote  control  with  undo. Command onCommand. offCommands.

} // … } 20 . undoCommand = onCommands[slot].execute().execute().undo(). } public void offButtonWasPushed(int slot) { offCommands(slot]. } public void undoButtonwWasPushed() { undoCommand.Adding  Undo   public void onButtonWasPushed(int slot) { onCommands(slot]. undoCommand = offCommands[slot].

//code to set fan to medium } // … public void off() { speed = OFF. public CeilingFan(String location) { this. LOW= 1. } public void high() { speed = HIGH. //code to turn fan off } 21 .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.location = location. speed = OFF. MEDIUM = 2. //code to set fan to high } public void medium() { speed = MEDIUM. OFF= 0.

Adding  Undo  For  Ceiling  Fan   public class CeilingFanHighCommand implements Command { CeilingFan ceilingFan.high().}} 22 . public CeilingFanHighCommand(CeilingFan ceilingFan) { this.OFF) { ceilingFan.getSpeed(). } else if (prevSpeed = CeilingFan. int prevSpeed. } else if (prevSpeed = CeilingFan.ceilingFan = ceilingFan.LOW) { ceilingFan. ceilingFan. } else if (prevSpeed = CeilingFan.medium().HIGH) { celingFan. } public void execute() { prevSpeed = { ceilingFan.low(). } public void undo() { if (prevSpeed == CeilingFan.

  •  Which  object  should  we  enhance  to  store  a  history  of  each   command  applied?  Why?   –  Client  (RemoteControlTest)   –  Receiver  (TV.History  of  Undo  OperaRons   •  If  the  Undo  buBon  is  pressed  mulRple  Rmes.   etc)   –   Invoker  (RemoteControl)   •  We  need  to  be  able  to  add  commands  to  a  list  and  then  later   get  the  most  recent  one.  DVD.  we  want  to  undo   each  command  that  had  been  previously  applied.  etc)   –  ConcreteCommand  (LightOnCommand.    What  kind  of  object  can  we  use?   23 .  LightOffCommand.

//this gets initialized in //constructor. public void onButtonWasPushed(int slot) { onCommands[slot]. } public void undoButtonWasPushed() { Command c = undoStack.push(offCommands[slot]).execute(). undoStack. c.pop().execute(). } 24 .undo(). } public void offButtonWasPushed(int slot) { offCommands[slot].History  of  Undo  OperaRons   public class RemoteControl { Stack<Command> undoStack. undoStack.push(onCommands[slot]).

  •  Which  object  should  we  enhance??   25 .Simple  Logging   •  We  want  to  enhance  the  Remote  Control  again  to  log   every  Rme  a  Command  is  executed  (on  or  off).

//Log here } public void offButtonWasPushed(int slot) { offCommands[slot]. //Log here } Advantage: We can add logging in the Invoker.execute().execute(). No change is needed in any of the Command or Receiver objects! 26 .Simple  Logging   Changes  to  RemoteControl…   public void onButtonWasPushed(int slot) { onCommands[slot].

 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.   27 ..       For  failure  recovery..  we  load  the  last  saved  document  and   re-­‐apply  the  persisted  commands.Complex  Logging   Let’s  say  we  had  a  spreadsheet  applicaRon  and  we  know  it   may  crash  oaen.

we can make these changes in one place (the Invoker) 28 . call execute() } Once again. } public void offButtonWasPushed(int slot) { offCommands[slot]. } public void RestoreCommands() { //load last saved state Commands[] storedCommands = GetCommandsFromDisk(). StoreToDisk(onCommands[slot]).Complex  Logging     public void onButtonWasPushed(int slot) { onCommands[slot]. //for each Command.execute().execute(). } public void SaveButtonPressed() { //Delete stored commands from disk. StoreToDisk(offCommands[slot]).

  •  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   modificaRon.Summary  so  far.   29 •  OO  Principles   .   –  Depend  on  abstracts..  Do  not  depend  on  concrete  classes.