Назначение
Инкапсулирует действие в объекте, таким образом позволяет сохранять это действие, передавать его как параметр, хранить историю выполнения или отменять. Допустим, в приложении есть слой визуализации и слой бизнес-логики. Слой визуализации отправляет некоторые запросы к слою бизнес-логики. Шаблон Command предлагает выполнять такие запросы не напрямую, а через инкапсулюрующие запрос команды. Команда включает в себя все детали запроса: на каком объекте он вызывается, какой метод вызывается, какие аргументы передаются. Например: на объекте “лампа в гостиной” вызвать метод “включить свет” с аргументами “теплый”, “50%”.
Описание
Действие инкапсулируется в некоторый объект – команду, который содержит в себе весь контекст, всю необходимую информацию, требуемую для выполнения этого действия. Используя сохраненный контекст можно так же реализовать отмену выполнения действия, применив обратное преобразование (правда, иногда такое преобразование не существует, или требует слишком много ресурсов). Все команды имеют общий интерфейс, это значит, что их можно хранить в одном контейнере и обрабатывать универсальным способом. Через интерфейс команды её можно выполнить, при этом в аргументы не потребуется передавать дополнительной информации, всё необходимое уже инкапсулировано в самой команде, а это значит, что команду можно выполнить из любого места.
Реализация
Особенность
У команды обычно нет возвращаемого типа, она модифицирует состояние приложения.
Примеры
Есть программируемый пульт дистанционного управления, на каждую кнопку которого можно назначить некоторое действие. При создании объекта пульта управления в него надо заинжектить команды, которые нажатие кнопок будет вызывать. Допустим, с помощью первой кнопки конкретного пульта мы хотим включать конкретную лампу зелёным цветом с яркостью 60%. Тогда под это действие создается объект команды с выбранными значениями параметров и привязанный к данной лампе. Теперь нажатие на кнопку будет вызывать установленную в пульте управления команду.
Варианты
- Можно реализовать макрокоманду, которая состоит из последовательности обычных команд и выполняет их при вызове.
- Интерфейс команды может включать или не включать метод для отмены команды.
Чем отличается
Adapter имеет схожую структуру и диаграмму классов с Command. Можно сказать, что команда это в некотором роде адаптер вызываемого объекта к интерфейсу Command. Получается, что Command – очень частный случай Adapter. Но шаблон Adapter применяется при необходимости, по ситуации к тому интерфейсу, к какому нужно, а под Command создается новый интерфейс. Adapter может адаптировать интерфейс с несколькими методами, обусловленными бизнес-логикой, Command реализует чисто командные методы.
Strategy определяет как делать действие, способ или алгоритм его выполнения. А Command определяет какое именно действие нужно сделать. Command часто не содержит логику самого действия, а просто инкапсулирует параметры и вызов на конкретном объекте некоторого метода. Strategy часто определяет именно содержательную часть логики, по которой выполняется действие.
Strategy - Quicksort / Mergesort - как
Command - Open / Close - что
Strategy обычно принимает параметры в методе, а команда инкапсулирует их в себе. Команда может выполняться отложено, Strategy в момент вызова. Strategy обычно несколько объектов (т.к. это штука без состояния), а объектов команды – множество, команда имеет состояние и завязана на конкретный объект, у которого она будет дергать метод.
Ссылки
https://java-design-patterns.com/patterns/command/
https://github.com/iluwatar/java-design-patterns/tree/master/command
https://refactoring.guru/design-patterns/command
Command Pattern – Design Patterns (ep 7)
StackOverflow: Difference Between Command and Adapter Patterns
StackOverflow: Difference between Strategy pattern and Command pattern