PHP Command Design Pattern Поведенческий паттерн

Паттерн Команда (Command) в PHP

Короткий скринкаст: паттерн «Команда» (Command) — превращаем запрос в объект. Пульт хранит команду и вызывает её, не зная, что именно произойдёт: включится или выключится свет.

Command Pattern — открыть страницу урока

Описание

Команда превращает запрос в отдельный объект с методом execute. Клиент создаёт команду и передаёт её инициатору — пульту. Пульт хранит команду и по нажатию кнопки просто вызывает execute, не зная ни о лампе, ни о том, что именно произойдёт. Сама команда держит ссылку на получателя — лампу — и решает, какой её метод вызвать. Так запрос становится самостоятельным объектом: его можно хранить, передавать и подменять.

UML-диаграмма

classDiagram class Command { <<interface>> +execute(): string } class Light { +on(): string +off(): string } class LightOnCommand { -light: Light +execute(): string } class LightOffCommand { -light: Light +execute(): string } class RemoteControl { -command: Command +setCommand(command: Command) +pressButton(): string } Command <|-- LightOnCommand : implements Command <|-- LightOffCommand : implements LightOnCommand o-- Light : receiver LightOffCommand o-- Light : receiver RemoteControl o-- Command : invokes

Код

Command.php

interface Command
{
    public function execute(): string;
}

Light.php

class Light
{
    public function on(): string
    {
        return 'Light is on';
    }

    public function off(): string
    {
        return 'Light is off';
    }
}

LightOnCommand.php

class LightOnCommand implements Command
{
    public function __construct(private Light $light) {}

    public function execute(): string
    {
        return $this->light->on();
    }
}

LightOffCommand.php

class LightOffCommand implements Command
{
    public function __construct(private Light $light) {}

    public function execute(): string
    {
        return $this->light->off();
    }
}

RemoteControl.php

class RemoteControl
{
    private ?Command $command = null;

    public function setCommand(Command $command): void
    {
        $this->command = $command;
    }

    public function pressButton(): ?string
    {
        return $this->command?->execute();
    }
}

demo.php

require_once 'Command.php';
require_once 'Light.php';
require_once 'LightOnCommand.php';
require_once 'LightOffCommand.php';
require_once 'RemoteControl.php';

$light = new Light;
$remote = new RemoteControl;

$remote->setCommand(new LightOnCommand($light));
echo $remote->pressButton().PHP_EOL;

$remote->setCommand(new LightOffCommand($light));
echo $remote->pressButton().PHP_EOL;

Пример результата

После php demo.php пульт по очереди выполнил две команды, а лампа включилась и выключилась:

Light is on
Light is off

Преимущества

  • Превращает запрос в объект: команду можно хранить, передавать и логировать.
  • Инициатор не знает ни получателя, ни самой операции.
  • Новые команды добавляются без правок пульта и клиента.
  • Позволяет легко реализовать очередь, отмену и повтор операций.

Когда использовать

  • Когда нужно параметризовать объект действием.
  • Когда запросы следует ставить в очередь, логировать или отменять.
  • Когда инициатор и получатель не должны знать друг о друге.
  • Когда одна и та же операция вызывается из разных мест.

Использование в Laravel

В Laravel Команда — это задания в очереди: класс из app/Jobs/* реализует handle(), а dispatch() кладёт его в очередь, откуда воркер вызывает выполнение, не зная деталей. Тот же принцип у класса Artisan-команд (Illuminate\Console\Command) с методом handle() и у диспетчера Illuminate\Bus\Dispatcher, который направляет команду к её обработчику.