Monday, June 30, 2014

Command pattern

Command pattern: Encapsulates a request as an object, thereby letting you parametrize
other objects with different requests, queue or log requests, and support
undoable operations.
.
The command pattern allows you to decouple the requester of an action from the object actually performing the action. By introducing 'command objects' into the application design, we can encapsulate a request to do something.
command pattern uml
(command pattern)
A nice example would be programming a software for a remote. This remote should work with various devices and therefore does not know what it should do for each device. For each button we will assign a command object that knows how to communicate with the right object (receiver) that gets the work done. Using an API (application programming interface) command objects can be loaded into button slots, allowing the remote code to stay very simple.
// every class you want to use as a command object will have
// to implement this interface
public interface Command {
  public void execute();
}
Each 'command object' is bound to a specific object before it is assigned to the remote (e.g., create one LightOnCommand object for the kitchen light and one for the living room light). 
public class LightOnCommand implements Command() {
  Light light; // receiver

  public LightOnCommand(Light light) {
    this.light = light;
  }

  public void execute() {
    light.on(); // here we execute everything we want to do with the receiver object
  }
}
public class SimpleRemoteControl() { // in the UML: invoker
  Command[] commands;

  public SimpleRemoteControl() {
    commands = new Command[5];
    // it might be a good idea to initialize all commands with a NullObject
    // (an object implementing the Command interface, which does nothing -
    // this way you wont have to check if an element of commands is null)
  }

  // the command object may be changed at runtime
  public void setCommand(int slot, Command command) {
    commands[slot] = command;
  }

  // if the remote button is pressed we call the execute method of
  // our Command object
  public void buttonWasPressed(int slot) {
    commands[slot].execute();
  }
} 
The remote object does not care what command object is in its slot, as long as it implements the Command interface. The command pattern is extremely useful as the following sections will show (besides what you have already read):

Undo
With the command pattern it is very easy to implement undo actions. Simply add another method to the Command interface. When implementing this method, perform the exact opposite action/s you implemented in execute. You will also have to add a reference to the last Command that was performed (set whenever method buttonWasPressed() is called), as well as a new method undoButtonWasPushed() to our SimpleRemoteControl class.
public interface Command {
  public void execute();
  public void undo();
}
public class LightOnCommand implements Command() {
  Light light; // receiver

  public LightOnCommand(Light light) {
    this.light = light;
  }

  public void execute() {
    light.on();
  }

  public void undo() {
    light.off(); // oposite action of turning a light on
  }
}
This is easy for objects, which have only two states. If there are more states you will have to maintain a variable storing the previous state every time a new state is set. In case of an undo action you will just have to set the current state equal to the previous state.
Sometimes you want to be able to press an undo button multiple times. To implement such an undo history, instead of keeping track of just the last Command executed, you keep a stack of previous commands. Every time the undo button is pressed, you can pop a command object off the stack and execute its undo method.

Macro command
If you have a situation where several commands should be executed at once you can still use our usual command interface, but use an Array of commands for the constructor. Neat isn't it?:
public class MacroCommand implements Command() {
  Command[] commands; // command array

  public MacroCommand(Command[] commands) {
    this.commands = command;
  }

  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();
    }
  }
}

Queuing requests
The command pattern offers the possibility to package a piece of computation and pass it around as first class object. The actual computation may be invoked long after some client application creates the command object. This is very nice if you are working with multiple threads. You can use this for schedulers, thread pools, job queues, ...
Furthermore threads don't even have to be assigned for specific tasks. One time a thread may be computing a math problem, and the next time it may be sending something over the network. Using the command pattern, the job queue classes are totally decoupled form the objects that are doing the computation.

Logging requests
You may store and load command objects on disk, using java object serialization. Take a spreadsheet application for example: You might want to implement a failure recovery by logging the actions on a spreadsheet rather than writing a copy of the spreadsheet to disk every time a change occurs.

I hope this post gives you an idea of the usefulness of the command pattern. See you next time.

No comments:

Post a Comment