Design patterns

The Command Pattern

The Command Pattern encapsulates a request as an object, thereby letting you parameterize other objects with different requests, queue or log requests, and support undoable operations.

The Command Pattern decouples an object making a request from the object that knows how to perform it. A Command object encapsulates a receiver with a set of actions. An invoker makes a request calling the Command object execute() method, which invokes actions on the receiver. So Invokers are parameterized with Commands. Commands may support the undo method that restores the object to its previous state before the execute() method was called. Macro Commands allow multiple commands to be invoked. Commands could be helpful when implementing logging and transactional systems (logging requests).

The command object encapsulates a request by binding together a set of actions on a specific receiver. It packages the actions and the receiver up into an object that exposes method, execute(). Other objects do not know what actions get performed on the receiver. They just call the execute() method and the request will be serviced.

Parameterizing an object with a command means that at any time (even during run time) we can provide invoker with any new command object, as long as it implemented the Command interface.

We can also easily support the Meta Command Pattern which allows us to create macros of commands so that we can execute multiple commands at once.

How it works (code examples):

Car ignition on/off example

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

Car ignition ON command implementation

public class IgnitionOnCommand implements Command {
    Engine engine;
    public IgnitionOnCommand(Engine engine) {
        this.engine = engine;
    }
    public void execute(){
        engine.on();
    }
    public void undo(){
        engine.off();
    }
}

Car ignition OFF command implementation

public class IgnitionOffCommand implements Command {
    Engine engine;
    public IgnitionOffCommand(Engine engine) {
        this.engine = engine;
    }
    public void execute(){
        engine.off();
    }
    public void undo(){
        engine.on();
    }
}

NO command implementation

public class NoCommand implements Command {
    public void execute() {}
}

MACRO command implementation

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

Car control panel (Invoker) implementation

public class CarControl {
    Command[] onCommands;
    Command[] offCommands;
    Command undoCommand;

    public CarControl() {
        onCommands = new Command[1];
        offCommands = new Command[1];

        Command noCommand = new NoCommand();
        onCommands[0] = noCommand;
        offCommands[0] = noCommand;
        undoCommand = noCommand;
    }
    public void setCommand(int Slot, Command onCommand, Command offCommand){
        onCommands[slot] = onCommand;
        offCommands[slot] = offCommand;
    }
    public void onButtonPressed(int slot) {
        onCommands[slot].execute();
        undoCommand = onCommands[slot];
    }
    public void offButtonPressed(int slot) {
        offCommands[slot].execute();
        undoCommand = offCommands[slot];
    }
    public void undoButtonPressed(int slot) {
        undoCommand.undo;
    }
}

Test application

public class IgnitionTest{
    public static void main(String[] args) {
        CarControl carControl = new CarControl();
        Engine engine = new Engine();
        IgnitionOnCommand ignitionOnCommand = new IgnitionOnCommand(engine);
        IgnitionOffCommand ignitionOffCommand = new IgnitionOffCommand(engine);

        Command[] ignitionTest = {ignitionOnCommand, ignitionOffCommand};
        MacroCommand ignitionTestMacro = new MacroCommand(ignitionTest);
        Command noCommand = new NoCommand();

        carControl.setCommand(0, ignitionOnCommand, ignitionOffCommand);

//lets start the car
        carControl.onButtonPressed(0);
//lets stop the car
        carControl.undoButtonPressed(0);
    }
//lets test ignition
        carControl.setCommand(0, ignitionTestMacro, noCommand);
        carControl.onButtonPressed(0);
}

Leave a Reply

Your email address will not be published. Required fields are marked *