Что это такое
Паттерн Command превращает действие в значение: в объект, который умеет выполнить сам себя — а часто и отменить сам себя. Действия-значения можно хранить в списках, ставить в очередь, откладывать, записывать в журнал и воспроизводить снова.
Как только вам нужна отмена, вам нужны команды: кто-то должен помнить, что было сделано, чтобы выполнить обратное действие.
Пример: набор текста с отменой
Каждое слово, добавленное в документ, — это команда с методами Execute() и Undo(). Выполненные команды попадают в список истории, и отмена — это просто просьба к последней команде обратить своё действие.
Действия, которые умеют обращать сами себя
Опечатка «мри» удаляется не специальным кодом, а той самой командой, которая её добавила: только она точно знает, что именно нужно забрать обратно.
Вывод: программа на Go и захваченный вывод
package main
import "fmt"
type Command interface {
Execute()
Undo()
}
type AddText struct {
doc *[]string
text string
}
func (c AddText) Execute() { *c.doc = append(*c.doc, c.text) }
func (c AddText) Undo() { *c.doc = (*c.doc)[:len(*c.doc)-1] }
func main() {
doc := []string{}
history := []Command{}
for _, word := range []string{"Привет", "мри"} {
cmd := AddText{doc: &doc, text: word}
cmd.Execute()
history = append(history, cmd)
}
fmt.Println("документ: ", doc)
// Последняя команда знает, как отменить саму себя.
last := history[len(history)-1]
last.Undo()
fmt.Println("после отмены:", doc)
}
Когда его использовать
Используйте для отмены и повтора действий, очередей действий, макросов и журналов аудита — везде, где действия нужно хранить или обращать.
Не используйте, когда действие достаточно просто запустить и забыть. В Go действие в очереди — это часто всего лишь значение-функция в канале, и полноценный паттерн оправдывает себя только тогда, когда нужна отмена или анализ действий.
Плюсы и минусы
Плюсы: отмена и повтор становятся естественными; действия можно ставить в очередь, записывать в журнал и воспроизводить; новые действия подключаются, не затрагивая вызывающий код.
Минусы: отдельный тип на каждое действие быстро накапливается; правильное состояние для отмены — тонкий момент: команда должна сохранить всё, что нужно для обращения её действия.