Что это такое
Паттерн Command (Команда) превращает действие в значение: в объект, который знает, как выполнить себя — и часто как отменить себя. Действия как значения можно хранить в списках, ставить в очередь, откладывать, записывать в журнал и воспроизводить заново.
Как только вам нужна отмена, вам нужны команды: что-то должно помнить, что было сделано, чтобы сделать обратное.
Пример: набор текста с отменой
Каждое слово, добавленное в документ, — это команда с методами execute() и undo(). Выполненные команды попадают в список истории, а отмена — это просто просьба к последней из них обратить своё действие.
Действия, которые могут обратить сами себя
Опечатка «wrold» удаляется не особым кодом, а той командой, которая её добавила, — только она точно знает, что нужно забрать назад. array_pop снимает последнюю команду с истории.
Вывод: PHP-скрипт
<?php
class Document {
/** @var string[] */
public array $words = [];
}
interface Command {
public function execute(): void;
public function undo(): void;
}
class AddText implements Command {
public function __construct(
private Document $doc,
private string $text,
) {}
public function execute(): void {
$this->doc->words[] = $this->text;
}
public function undo(): void {
array_pop($this->doc->words);
}
}
$doc = new Document();
$history = [];
foreach (["Hello", "wrold"] as $word) {
$cmd = new AddText($doc, $word);
$cmd->execute();
$history[] = $cmd;
}
echo "документ: ", implode(" ", $doc->words), "\n";
// Последняя команда знает, как отменить саму себя.
$last = array_pop($history);
$last->undo();
echo "после отмены: ", implode(" ", $doc->words), "\n";
Когда его применять
Применяйте для отмены и повтора, очередей действий, макросов и журналов аудита — везде, где действия нужно хранить или обращать.
Обойдитесь без него, когда действия выполняются по принципу «запустил и забыл». В PHP простое отложенное действие — это часто просто замыкание в массиве; полный паттерн оправдывает себя только тогда, когда нужна отмена или разбор действий.
Плюсы и минусы
Плюсы: отмена и повтор становятся естественными; действия можно ставить в очередь, записывать в журнал и воспроизводить; новые действия подключаются без изменения вызывающей стороны.
Минусы: по одному классу на действие — их быстро становится много; корректное состояние для отмены обманчиво сложно: команда должна сохранить всё, что нужно для обращения её действия.