Design patterns: Command

Mauto 3 min

The Command Pattern is a behavioral pattern that turns a request into a standalone object, letting you parameterize methods with different requests, queue or log requests, and support undoable operations (undo). The pattern separates the object that sends the request from the one that executes it, improving code flexibility and maintainability.

Strengths

  1. Separation of roles: It decouples the invoker (who sends the command) from the receiver (who executes it), improving modularity and letting you change the classes independently of one another.
  2. Support for undoable operations (Undo/Redo): The pattern lets you store the state before a command is executed, making it easier to implement undoable or repeatable operations.
  3. Easy to extend: Adding new commands is simple, since each command is encapsulated in a separate class that implements a common interface. This makes the system highly extensible.
  4. Flexible queuing and logging: It lets you queue, delay or log commands, creating systems where the execution of requests can be postponed or monitored.
  5. Parameterized requests: Requests can be treated as objects and passed as parameters, enabling a level of abstraction and dynamic configuration of the operations to be performed.

Use Case

Suppose we want to manage a simple remote control system for electronic devices, such as a TV and lights. Each device can receive commands such as “Turn on” or “Turn off”. We use the Command Pattern to model these commands.

PHP Code

// Command interface
interface Command {
    public function execute();
}

// Receiver - TV device class
class TV {
    public function turnOn() {
        echo "TV on\n";
    }

    public function turnOff() {
        echo "TV off\n";
    }
}

// Receiver - Light class
class Light {
    public function turnOn() {
        echo "Light on\n";
    }

    public function turnOff() {
        echo "Light off\n";
    }
}

// Command to turn on the TV
class TurnOnTVCommand implements Command {
    private $tv;

    public function __construct(TV $tv) {
        $this->tv = $tv;
    }

    public function execute() {
        $this->tv->turnOn();
    }
}

// Command to turn off the TV
class TurnOffTVCommand implements Command {
    private $tv;

    public function __construct(TV $tv) {
        $this->tv = $tv;
    }

    public function execute() {
        $this->tv->turnOff();
    }
}

// Command to turn on the light
class TurnOnLightCommand implements Command {
    private $light;

    public function __construct(Light $light) {
        $this->light = $light;
    }

    public function execute() {
        $this->light->turnOn();
    }
}

// Command to turn off the light
class TurnOffLightCommand implements Command {
    private $light;

    public function __construct(Light $light) {
        $this->light = $light;
    }

    public function execute() {
        $this->light->turnOff();
    }
}

// Invoker - The remote control
class RemoteControl {
    private $commands = [];

    public function setCommand($slot, Command $command) {
        $this->commands[$slot] = $command;
    }

    public function pressButton($slot) {
        if (isset($this->commands[$slot])) {
            $this->commands[$slot]->execute();
        } else {
            echo "No command assigned to button $slot\n";
        }
    }
}

// Usage
$tv = new TV();
$light = new Light();

$remoteControl = new RemoteControl();
$remoteControl->setCommand(1, new TurnOnTVCommand($tv));
$remoteControl->setCommand(2, new TurnOffTVCommand($tv));
$remoteControl->setCommand(3, new TurnOnLightCommand($light));
$remoteControl->setCommand(4, new TurnOffLightCommand($light));

// Execute the commands via the remote control
$remoteControl->pressButton(1); // TV on
$remoteControl->pressButton(3); // Light on
$remoteControl->pressButton(4); // Light off
$remoteControl->pressButton(2); // TV off

Explanation

  • Command is the interface that all commands must implement.
  • TurnOnTVCommand, TurnOffTVCommand, TurnOnLightCommand and TurnOffLightCommand are the concrete command implementations, each representing a specific action.
  • RemoteControl is the invoker that triggers the commands without knowing exactly how they work.
  • This example makes it easy to expand the system, adding new devices or new commands simply by creating new command classes.

Conclusion

The Command Pattern is a great tool for decoupling the client that invokes an action from the code that performs it. This improves code maintainability, makes it easy to implement undoable operations (undo/redo) and supports flexible use of requests.

BACK TO THE GUIDE

Copiato