Другие названия: Action (Действие), Transaction (Транзакция).

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

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

Решение. Паттерн Команда предлагает отправлять запросы неизвестным объектам приложения, преобразовав сам запрос в объект. Этот объект можно хранить и передавать, как и любой другой. В основе данного паттерна лежит абстрактный класс Command, в котором объявлен интерфейс для выполнения операций. В простейшей своей форме этот интерфейс состоит из одной абстрактной операции Execute. Конкретные подклассы Command определяют пару «получатель-действие», сохраняя получателя в переменной экземпляра, и реализуют операцию Execute так, чтобы она посылала запрос. У получателя же есть информация, необходимая для выполнения запроса.

С помощью объектов Command легко реализуется меню. Каждый пункт меню – это экземпляр класса MenuItem. Сами меню и все их пункты создает класс Application наряду со всеми остальными элементами пользовательского интерфейса. Класс Application отслеживает также открытые пользователем документы. Приложение конфигурирует каждый объект MenuItem экземпляром конкретного подкласса Command. Когда пользователь выбирает некоторый пункт меню, ассоциированный с ним объект MenuItem вызывает операцию Execute для своего объекта-команды, а Execute выполняет нужные действия.

 

 

Объекты MenuItem не имеют информации о том, какой подкласс класса Command они используют. Подклассы Command хранят информацию о получателе запроса и вызывают одну или несколько операций этого получателя.

Например, подкласс PasteCommand может поддерживать вставку текста из буфера обмена в документ. Получателем для PasteCommand является Document, который был передан при создании объекта. Операция Execute вызывает операцию Paste документа-получателя.

 

 

Для подкласса OpenCornmand операция Execute ведет себя по-другому: она запрашивает у пользователя имя документа, создает соответствующий объект Document, извещает о новом документе приложение-получатель и открывает этот документ.

 

 

Иногда объект MenuItem должен выполнить последовательность команд. Например, пункт меню для центрирования страницы стандартного размера можно было бы сконструировать сразу из двух объектов: CenterDocumentCommand и NormalsizeCommand. Поскольку такое комбинирование команд – явление обычное, то мы можем определить класс MacroCommand, позволяющий объекту MenuItem выполнять произвольное число команд. MacroCommand – это конкретный подкласс класса Command, который просто выполняет последовательность команд. У него нет явного получателя, поскольку для каждой команды определен свой собственный исполнитель.

 

 

В каждом из приведенных примеров паттерн Команда отделяет объект, инициирующий операцию, от объекта, который может ее выполнить. Это позволяет добиться высокой гибкости при проектировании пользовательского интерфейса. Пункт меню и кнопка одновременно могут быть ассоциированы в приложении с некоторой функцией, для этого достаточно приписать обоим элементам один и тот же экземпляр конкретного подкласса класса Command. Мы можем динамически подменять команды, что очень полезно для реализации контекстно-зависимых меню. Можно также поддерживать сценарии, если компоновать простые команды в более сложные. Все это выполнимо, потому что объект, инициирующий запрос, должен располагать информацией лишь о том, как его отправить, а не о том, как его выполнить.

Общая структура решения.

 

 

Command – команда: объявляет интерфейс для выполнения операции.

ConcreteCommand (PasteCommand, OpenCommand) – конкретная команда: определяет связь между объектом-получателем Receiver и действием; реализует операцию Execute путем вызова соответствующих операций объекта Receiver.

Invoker (MenuItem) – инициатор: обращается к команде для выполнения запроса.

Receiver (Document, Application) – получатель: располагает информацией о способах выполнения операций, необходимых для удовлетворения запроса.

Приложение создает объект ConcreteCommand и устанавливает для него получателя, а также передает указатель на эту команду соответствующему инициатору. Инициатор Invoker отправляет запрос, вызывая операцию команды Execute. Если требуется, например, поддерживать отмену выполненных действий, то ConcreteCommand перед вызовом Execute сохраняет информацию о состоянии, достаточную для выполнения отката.

Применимость. Используйте паттерн Команда, когда:

Результаты:

- команда разрывает связь между объектом, инициирующим операцию, и объектом, имеющим информацию о том, как ее выполнить;

- команды – это самые настоящие объекты, допускается манипулировать ими и расширять их точно так же, как в случае с любыми другими объектами;

- из простых команд можно собирать составные, для этого можно применить паттерн Компоновщик;

- добавлять новые команды легко, поскольку никакие существующие классы изменять не нужно.

Особенности реализации. При реализации паттерна Команда следует обратить внимание на следующие аспекты:

Родственные паттерны.

Паттерн Компоновщик можно использовать для реализации макрокоманд.

Паттерн Хранитель может помочь сохранять состояние команды, необходимое для отмены ее действия.

Команда, которую нужно копировать перед помещением в список истории, ведет себя, как Прототип.