Command
Паттерн, превращающий действие в объект — чтобы его можно было передать, поставить в очередь, залогировать и отменить.
Command инкапсулирует запрос в виде объекта, отделяя отправителя действия от исполнителя и позволяя ставить запросы в очередь, логировать и отменять их.
Какую задачу решает
Кнопка «Сохранить» должна вызвать сохранение. Но если зашить вызов прямо в кнопку, её не переиспользовать и не отменить действие. Command превращает «что сделать» в объект с методом execute() (и часто undo()). Теперь действие можно хранить в истории, ставить в очередь, повторять и откатывать.
Идея и реализация
Реализуем редактор текста с командами и историей отмены.
class Editor:
def __init__(self):
self.text = ""
class AppendCommand:
def __init__(self, editor, value):
self.editor = editor
self.value = value
def execute(self):
self.editor.text += self.value
def undo(self):
self.editor.text = self.editor.text[:-len(self.value)]
class History:
def __init__(self):
self._done = []
def run(self, command):
command.execute()
self._done.append(command)
def undo(self):
if self._done:
self._done.pop().undo()
editor = Editor()
history = History()
history.run(AppendCommand(editor, "Привет"))
history.run(AppendCommand(editor, ", мир"))
print(editor.text)
history.undo() # откат последней команды
print(editor.text)
Вывод:
Привет, мир Привет
Каждое действие — отдельный объект, который умеет себя выполнить и откатить. История хранит стек выполненных команд; undo() снимает верхнюю и вызывает её откат. Отправитель (кнопка/история) не знает деталей — он работает с интерфейсом команды.
Ключевая ценность — развязка во времени и пространстве. Раз действие стало объектом, момент его создания и момент выполнения можно развести: команду создают здесь и сейчас, а исполняют позже, в другом потоке или вообще на другой машине (если команду сериализовать). Так Command становится основой очередей задач: продюсер кладёт команды в очередь, воркеры достают и вызывают execute(), ничего не зная об источнике.
Для полноценного redo достаточно вести второй стек: при undo снятую команду кладём в «отменённые», при redo — выполняем верхнюю из него заново. А если откат сложного действия нельзя выразить обратной операцией, команду часто комбинируют с паттерном Memento: перед выполнением она сохраняет снимок состояния, а в undo() просто восстанавливает его.
Плюсы и минусы
- Плюс: undo/redo, очереди задач, отложенное и повторное выполнение, логирование действий «из коробки».
- Минус: на каждое действие — класс; для простых случаев избыточно (в Python спасают функции/замыкания).
Где встречается
Undo/redo в редакторах, очереди фоновых задач (Celery-подобные), макросы (последовательность команд), транзакции, кнопки и горячие клавиши в GUI, паттерн «job/task» в системах обработки.
Итог
- Command превращает запрос в объект с
execute()и частоundo(). - Развязывает отправителя и исполнителя.
- Даёт историю, очереди, отмену и повтор действий.