Friday, May 20, 2011

Command pattern

Definition
Command pattern is a type of behavioural pattern. Following defines the command pattern:
Encapsulate a request as an object, thereby letting you 
parameterize clients with different requests, queue or
log requests, and support undo-able operations



Terms you must know before we start on it


Three terms always associated with the command pattern are client, invoker and receiver. The client instantiates the command object and provides the information required to call the method at a later time. The invoker decides when the method should be called. The receiver is an instance of the class that contains the method's code.

Advantage of Command Pattern

Using the command pattern helps you to :


  • Decouple the object that invokes the operation from the one that performs the action.
    described earlier.
  • Assemble commands into a composite command. An example is the MacroCommand class. Composite commands are an instance of the Composite pattern.
  • Add new Commands, without having to change existing classes.

Command Pattern in UML

command

Make Operation and Operand interface for greater flexibility.


Implementing Command Pattern in java


Consider the class diagram:


commandPattern_java


The following is a simple description of each of the elements of the above diagram, followed by a simple implementation.


Client: The client is responsible for creating the Command object and setting it's reciever.


public class ClientApp {
public static void main(String[] args) {
Receiver rec = new Receiver();
Command incCommand = new IncrementCommand(rec);
Command decCommand = new DecrementCommand(rec);
Invoker invoker = new Invoker();
invoker.setDecCommand(decCommand);
invoker.setIncCommand(incCommand);
invoker.addRequest();
invoker.addRequest();
invoker.removeRequest();
System.out.println(rec.getValue());
}
}


Invoker: The Invoker acts as a placeholder for the Command object and invokes the execute method on the Command. In case of undoable commands, it stores the command in a stack (for multi-level undo, or just the command for single level undo), before executing the command.


public class Invoker {
Stack<Command> commands;

Command incCommand;

Command decCommand;

public Invoker() {
commands = new Stack<Command>();
}

public void setIncCommand(Command command) {
incCommand = command;
}

public void setDecCommand(Command command) {
decCommand = command;
}

public void undoAll() {
Command cmd = null;
while (!commands.empty()) {
cmd = commands.pop();
cmd.undo();
}
}

public void addRequest() {
incCommand.execute();
commands.add(incCommand);
}

public void removeRequest() {
decCommand.execute();
commands.add(decCommand);

}

public void commit() {
commands = new Stack<Command>();
}
}


Receiver: The object that performs the operations associated with carrying out a request. Any class may serve as a Receiver.


public class Receiver {
private int value;

public Receiver() {
value = 0;
}

public void increment() {
++value;

}

public void decrement() {
--value;
}

public int getValue() {
return value;
}

}


Command: The command object represents the request operation. The command implements execute() method, which invokes the corresponding operations on the Reciever. This defines a binding between a Receiver object and an action.


public interface Command {
public void execute();
public void undo();
}

 

public class IncrementCommand implements Command {

Receiver receiver;

public IncrementCommand(Receiver rec) {
receiver = rec;
}

public void execute() {
receiver.increment();

}

public void undo() {
receiver.decrement();
}

}



public class DecrementCommand implements Command {

Receiver receiver;
public DecrementCommand(Receiver receiver) {
this.receiver = receiver;
}

public void execute() {
receiver.decrement();
}

public void undo() {
receiver.increment();
}
}


Additional Notes


  • A command can have a wide range of abilities from a simple interface between the client and receiver to being a receiver itself.
  • When supporting multi-level undo, a command may store state information, which mean that, with each execute(), you have to copy the state of the command at that time. In such cases a copy of the command has to be added to the history stack.


1 comment:

  1. Nice article with example as well. Thanks :)

    ReplyDelete

Chitika