Паттерн Команда
Паттерн Команда (Command) выполняет преобразование запроса в объект, обеспечивая:
q параметризацию клиентов с различными запросами;
q постановку запросов в очередь и их регистрацию;
q поддержку отмены операций.
Проблема. Достаточно часто нужно посылать запрос, не зная, выполнение какой конкретной операции запрошено и кто является получателем запроса. В этих случаях следует отделить объект, инициирующий запрос, от объекта, способного выполнить запрос. В результате обеспечивается высокая гибкость разработки пользовательского интерфейса — можно связывать различные пункты меню с определенной функцией, динамически подменять команды и т. д. Паттерн Команда применяется в следующих случаях:
q объекты параметризируются действием. В процедурных языках параметризация осуществляется при помощи функции обратного вызова, которая регистрируется для последующего вызова. Паттерн Команда предлагает объектно-ориентированную замену функций обратного вызова;
q необходимо обеспечить отмену операций. Это возможно благодаря хранению истории выполнения операций;
q необходимо регистрировать изменения состояния для восстановления системы в случае аварийного отказа;
q необходимо создание сложных операций, которые строятся на основе примитивных операций.
Решение. Основным элементом решения является абстрактный класс Команда, обеспечивающий одну абстрактную операцию Выполнять(). Конкретные подклассы этого класса реализуют операцию Выполнять(). Они задают пару получатель-действие. Получатель запоминается в экземплярной переменной подкласса. Запрос получателю посылается в ходе исполнения конкретной операции Выполнять().
Структурная составляющая паттерна Команда показана на рис. 12.54. Классы этой структуры имеют следующие обязанности:
q Команда объявляет интерфейс для выполнения операции;
q КонкрКоманда определяет связь между экземпляром класса Получатель и действием, реализует Выполнять(), вызывая нужную операцию получателя;
q Клиент создает объект класса КонкрКоманда и устанавливает его получателя;
q Инициатор просит команду выполнить запрос;
q Получатель умеет выполнять запрашиваемые операции.
Рис. 12.54. Структурная составляющая паттерна Команда
В качестве конкретной команды могут выступать команда Открыть, команда Вставить. Инициатором может быть Пункт Меню, а получателем — Документ.
Объекты этого паттерна осуществляют следующие взаимодействия:
q клиент создает объект класса КонкрКоманда и задает его получателя;
q объект класса Инициатор сохраняет объект класса КонкрКоманда;
q инициатор вызывает операцию Выполнять() объекта класса КонкрКоманда;
q объект класса КонкрКоманда вызывает операцию своего получателя для исполнения запроса.
Результаты. Применение паттерна Команда приводит к следующему:
q объект, запрашивающий операцию, отделяется от объекта, умеющего выполнять запрос;
q объекты-команды являются полноценными объектами. Их можно использовать и расширять обычным способом;
q из простых команд легко компонуются составные команды;
q легко добавляются новые команды (изменять существующие классы при этом не требуется).
Обозначение паттерна Команда приведено на рис. 12.55, где показано, что у него четыре параметра настройки — клиент, команда, инициатор и получатель.
Рис. 12.55. Обозначение паттерна Команда
Настройку паттерна на приложение с графическим меню иллюстрирует рис. 12.56.
Рис. 12.56. Настройка паттерна Команда
Очевидно, что в получаемой кооперации конкретный класс Редактор играет роль клиента, классы КомандаОткрыть и КомандаВставить становятся классами Конкретных Команд (и подклассами абстрактного класса Команда), класс ПунктМеню замещает класс Инициатор паттерна, а конкретный класс Документ замещает класс Получатель паттерна.