CodeLAB
на главную карта сайта обратная связь
каталог | задачи | паттерны | исходники | стат | форумы | ссылки
 гость
искать в
Главная >> Каталог задач >> Паттерны >> Поведения >> Команда (Command)

<< назад
распечатать обсудить >>


Команда (Command)
реализации: java, количество: 5

Aвтор: this
Дата: 04.10.2007
Просмотров: 66351
Рейтинг: 0/0,0(0)
+
реализации(исходники) +добавить

Имя

«Паттерн
Command» оригинал codelab.ru источник codelab.ru

Команда- паттерн поведения объектов, инкапсулирующий различные алгоритмы в единую сущность. Встречается также под именами Action (действие), Transaction (транзакция). источник codelab.ru codelab.ru оригинал

Условия, Задача, Назначение

Инкапсулирует запрос на выполнение конкретной логики (алгоритма) – в виде объекта, позволяя тем самым задавать параметры клиентов для обработки соответствующих запросов, ставить запросы в очередь или протоколировать их, а также поддерживать отмену операций.

Мотивация

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

menu commands codelab.ru источник оригинал codelab.ru

С помощью объектов Command легко, например, реализуются меню. Каждый пункт меню - это экземпляр класса Menultem. Сами меню и все их пункты создает класс Application наряду со всеми остальными элементами пользовательского интерфейса. Класс Application отслеживает также открытые пользователем документы. источник codelab.ru codelab.ru оригинал
Приложение конфигурирует каждый объект MenuItem экземпляром конкретного подкласса Command. Когда пользователь выбирает некоторый пункт меню, ассоциированный с ним объект Menultem вызывает Execute для своего объекта-команды, a Execute выполняет операцию. Объекты Menultem не имеют информации, какой подкласс класса Command они используют (просто лишь вызывая метод выполнения Execute). Подклассы Command хранят информацию о получателе запроса и вызывают одну или несколько операций этого получателя. оригинал источник codelab.ru codelab.ru
Например, подкласс PasteCommand поддерживает вставку текста из буфера обмена в документ. Получателем для PasteCommand является Document, который был передан при создании объекта. Операция Execute вызывает операцию Paste документа-получателя. оригинал codelab.ru источник codelab.ru

codelab.ru источник codelab.ru оригинал

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

codelab.ru источник оригинал codelab.ru

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

источник codelab.ru оригинал codelab.ru

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

Признаки применения, использования паттерна Команда (Command)

Используйте паттерн команда, когда хотите: codelab.ru источник оригинал codelab.ru
  1. Параметризовать объекты выполняемым действием.
    Как в случае с пунктами меню Menultem. В процедурном языке такую параметризацию можно выразить с помощью функции обратного вызова, то есть такой функции, которая регистрируется (динамически компонуется и создается таким образом в приложении), чтобы быть вызванной позднее. Команды представляют собой объектно-ориентированную альтернативу функциям обратного вызова. оригинал codelab.ru codelab.ru источник
  2. Определять, ставить в очередь и выполнять запросы в разное время.
    Время жизни объекта Command необязательно должно зависеть от времени жизни исходного запроса. Если получателя запроса удается реализовать так, чтобы он не зависел от адресного пространства, то объект-команду можно передать другому процессу, который займется его выполнением. Либо реализовать так что получатель не будет зависеть также от runtime-среды выполнения (виртуальной машины или сервера например) – то объект команду вполне можно сериализовать передать по какому-либо протоколу, далее десериализовать и выполнить уже на удаленном конце другим получателем. оригинал codelab.ru источник codelab.ru
  3. Поддержать отмену операций.
    Операция Execute объекта Command может сохранить состояние, необходимое для отката действий, выполненных командой. В этом случае в интерфейсе класса Command должна быть дополнительная операция Unexecute, которая отменяет действия, выполненные предшествующим обращением к Execute. Выполненные команды хранятся в списке истории. Для реализации произвольного числа уровней отмены и повтора команд нужно обходить этот список соответственно в обратном и прямом направлениях, вызывая при посещении каждого элемента команду Unexecute или Execute. оригинал источник codelab.ru codelab.ru
  4. Поддержать протоколирование изменений (лог).
    Чтобы их можно было выполнить повторно после аварийной остановки системы. Дополнив интерфейс класса Command операциями сохранения и загрузки, вы сможете вести протокол изменений во внешней памяти. Для восстановления после сбоя нужно будет загрузить сохраненные команды с диска и повторно выполнить их с помощью операции Execute. codelab.ru источник codelab.ru оригинал
  5. Структурировать систему на основе высокоуровневых операций, построенных из примитивных.
    Такая структура типична для информационных систем, поддерживающих транзакции. Транзакция инкапсулирует целый набор изменений данных. паттерн команда позволяет моделировать транзакции. У всех команд есть общий интерфейс, что дает возможность работать одинаково с любыми транзакциями. С помощью этого паттерна можно легко добавлять в систему новые виды транзакций. codelab.ru codelab.ru источник оригинал

Решение

оригинал источник codelab.ru codelab.ru

источник codelab.ru codelab.ru оригинал

Участники паттерна Команда (Command)

оригинал codelab.ru источник codelab.ru

  1. Command – команда.
    Объявляет интерфейс для выполнения операций. codelab.ru codelab.ru оригинал источник
  2. ConcreteCommand (PasteCommand, OpenCommand) - конкретная команда.
    Определяет связь между объектом-получателем Receiver и действием.
    Реализует операцию Execute путем вызова соответствующих операций объекта Receiver; оригинал codelab.ru codelab.ru источник
  3. Client (Application) – клиент.
    Создает объекты класса ConcreteCommand и устанавливает их получателей. codelab.ru оригинал источник codelab.ru
  4. Invoker (Menultem) – инициатор.
    Обращается к команде для выполнения запроса. codelab.ru источник оригинал codelab.ru
  5. Receiver (Document, Application) – получатель.
    Располагает информацией о способах выполнения операций, необходимых для удовлетворения запроса. В роли получателя может выступать любой класс. оригинал codelab.ru codelab.ru источник

Схема использования паттерна Команда (Command)

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

На следующей диаграмме видно, как Command разрывает связь между инициатором и получателем (а также запросом, который должен выполнить последний): codelab.ru источник оригинал codelab.ru

codelab.ru codelab.ru оригинал источник

Вопросы, касающиеся реализации паттерна Команда (Command)

При реализации следует обратить внимание на следующие моменты: codelab.ru оригинал источник codelab.ru
  1. Насколько «умной» должна быть команда.
    У команды может быть широкий круг обязанностей. На одном полюсе стоит простое определение связи между получателем и действиями, которые нужно выполнить для удовлетворения запроса. На другом – независимая команда, т.е. реализация всего самостоятельно, без обращения за помощью к получателю. Последний вариант полезен, когда вы хотите определить команды, не зависящие от существующих классов, когда подходящего получателя не существует или когда получатель команде точно не известен. Например, команда, создающая новое окно приложения, может не понимать, что именно она создает, а трактовать окно, как любой другой объект. Где-то посередине между двумя крайностями находятся команды, обладающие достаточной информацией для динамического обнаружения своего получателя. codelab.ru источник оригинал codelab.ru
  2. Поддержка отмены и повтора операций.
    Команды могут поддерживать отмену и повтор операций, если имеется возможность отменить результаты выполнения (например, операцию Unexecute или Undo). В классе ConcreteCommand может сохраняться необходимая для этого дополнительная информация, в том числе:
    - объект-получатель Receiver, который выполняет операции в ответ на запрос;
    - аргументы операции, выполненной получателем;
    - исходные значения различных атрибутов получателя, которые могли измениться в результате обработки запроса.
    Получатель должен предоставить операции, позволяющие команде вернуться в исходное состояние. Для поддержки всего одного уровня отмены приложению достаточно сохранять только последнюю выполненную команду. Если же нужны многоуровневые отмена и повтор операций, то придется вести список истории выполненных команд. Максимальная длина этого списка и определяет число уровней отмены и повтора. Проход по списку в обратном направлении и откат результатов всех встретившихся по пути команд отменяет их действие.
    Проход в прямом направлении и выполнение встретившихся команд приводит к повтору выполнения действий.
    Команду, допускающую отмену, возможно, придется клонировать (побайтно) перед помещением в список истории. Дело в том, что объект команды, использованный для доставки запроса, скажем от пункта меню MenuItem, позже мог быть использован для других запросов. Поэтому копирование необходимо, чтобы определить разные вызовы одной и той же команды, если ее состояние при любом вызове может изменяться.
    Например, команда DeleteCoinmand, которая удаляет выбранные объекты, при каждом вызове должна сохранять разные наборы объектов. Поэтому объект DeleteCommand необходимо скопировать после выполнения, а копию поместить в список истории. Если в результате выполнения состояние команды никогда не изменяется, то копировать не нужно - в список достаточно поместить лишь ссылку на команду. Команды, которые обязательно нужно копировать перед помещением в список истории, ведут себя подобно прототипам. оригинал источник codelab.ru codelab.ru
  3. Накопление ошибок в процессе отмены.
    При обеспечении надежного, сохраняющего семантику механизма отмены и повтора может возникнуть проблема гистерезиса. При выполнении, отмене и повторе команд иногда накапливаются ошибки, в результате чего состояние приложения оказывается отличным от первоначального. Поэтому порой необходимо сохранять в команде больше информации, дабы гарантировать, что объекты будут целиком восстановлены. Чтобы предоставить команде доступ к этой информации, не раскрывая внутреннего устройства объектов, можно воспользоваться паттерном хранитель. codelab.ru codelab.ru источник оригинал
  4. Применение шаблонов в C++.
    Для команд, которые не допускают отмену и не имеют аргументов, в языке C++ можно воспользоваться шаблонами, чтобы не создавать подкласс класса Command для каждой пары действие-получатель. оригинал источник codelab.ru codelab.ru

Результаты

Результаты применения паттерна команда следующие: codelab.ru оригинал codelab.ru источник
  1. Команда разрывает связь между объектом, инициирующим операцию, и объектом, имеющим информацию о том, как ее выполнить. codelab.ru оригинал codelab.ru источник
  2. Команды - это самые настоящие объекты.
    Допускается манипулировать ими и расширять их точно так же, как в случае с любыми другими объектами, наследовать, компоновать, агрегировать для получения новых команд или модификации имеющихся. codelab.ru источник codelab.ru оригинал
  3. Из простых команд можно собирать составные.
    Например, класс MacroCommand, рассмотренный выше. В общем случае составные команды описываются паттерном компоновщик. codelab.ru источник codelab.ru оригинал
  4. Добавлять новые команды легко.
    Поскольку никакие существующие классы изменять не нужно. codelab.ru codelab.ru оригинал источник

Пример

В отличие от традиционных материалов, когда в качестве примера использования наперебой рассматривают реализацию через команды пунктов меню (главного и контекстного) – приведем другой не менее частый случай. В настоящее время довольно повсеместно используется идея модульного, взаимозаменяемого программирования, при котором у нас есть некий единый каркас приложения, а дополнительная функциональность реализуется в виде отдельных, независимых классов-модулей. При этом нет никаких взаимосвязей между каркасом и модулями, все полностью независимо: каркас лишь конфигурируется на этапе загрузки определенным количеством модулей далее по запросу пользователя, он вызывает на выполнение определенный модуль или сразу несколько, взаимодействуя с ними через единый «модульный» интерфейс, в котором обычно достаточно одной какой-либо операции запуска, типа Execute(), Run() и т.д. оригинал источник codelab.ru codelab.ru
Таким образом, модуль – это и есть команда, Command. Его подклассы реализуют всю логику каждый своего модуля. источник codelab.ru оригинал codelab.ru

Соответственно, наша система имеет единый общий каркас и наша задача добавить необходимые нам модули. Модульный интерфейс имеет вид: Module , где GetMenu() – возвращает содержимое меню управления данным модулем. источник codelab.ru оригинал codelab.ru

Допустим, нам понадобится модуль – галерея картинок: при выполнении должны будут отображаться список альбомов, либо список закаченных картинок. Реализация в общих чертах может быть такой: ImageGallery. codelab.ru оригинал codelab.ru источник
Обратим внимание, что данный модуль самостоятельно работает с базой данных, соединение с которой он сам создает. Типичный случай паттерна-команда: ни весь каркас приложения, ни другие модули – могут даже никак и не знать о существовании этой базы данных, в случае если с ней работает только модуль галерея. Только этот модуль сам решает, где ему хранить свои данные и откуда их забирать – из базы ли, из файлов и т.д., остальное окружение никак не осведомлено о том как внутри себя модуль реализует свою логику. codelab.ru codelab.ru источник оригинал
Далее нам нужен модуль отображения отчетов по продажам: SalesReports. источник codelab.ru оригинал codelab.ru
Реализация аналогична. оригинал codelab.ru codelab.ru источник
Теперь допустим у нас полно простых информационных страниц системы (сайта), на которых нужно просто отображать из базы информацию по идентификатору этой страницы. Чтобы каждый раз не создавать новый подкласс команды и потом не заморачиваться с поддержкой большого количества одинаковых сущностей – здесь уместней всего применить подход шаблонной команды: InfoPage. codelab.ru источник оригинал codelab.ru
Т.е. теперь проблема этого всего множества однотипных страниц решается очень просто, нужно лишь сконфигурировать каркас соответствующими экземплярами класс InfoPage, указывая каждый раз конкретные значения contentID и передавая общее соединение с базой (открытое ранее). оригинал codelab.ru источник codelab.ru
Ну и напоследок, помимо всего этого, у нас будут страницы, на которых нужно запускать несколько модулей вместе, например, вверху показывать содержимое одной из информационных страниц, а ниже – выводить все альбомы галереи картинок. Этого мы добиваемся с помощью т.н. макрокоманды (MacroCommand), манипулирующий целым списком команд: MultiPage. codelab.ru codelab.ru источник оригинал
Как видно, макрокоманда также реализует интерфейс общий для всех команд, а при своем запуске просто последовательно вызывает каждую из команд из своего списка. codelab.ru источник codelab.ru оригинал

  codelab.ru источник оригинал codelab.ru

Известные применения паттерна Команда (Command)

Быть может, впервые паттерн команда появился в работе Генри Либермана Henry Lieberman. В системе МасАрр команды широко применяются для реализации допускающих отмену операций. В ЕТ++, InterViews и Unidraw также имеются классы, описываемые паттерном команда. Так, в библиотеке Interviews определен абстрактный класс Action, который определяет всю функциональность команд. Есть и шаблон ActionCallback, параметризованный действием Action, который автоматически инстанцирует подклассы команд. оригинал codelab.ru источник codelab.ru

В библиотеке классов THINK также используются команды для поддержки отмены операций. В THINK команды называются задачами (Tasks). Объекты Task передаются по цепочке обязанностей, пока не будут кем-то обработаны. источник codelab.ru оригинал codelab.ru
Объекты команд в каркасе Unidraw уникальны в том отношении, что могут вести себя подобно сообщениям. В Unidraw команду можно послать другому объекту для интерпретации, результат которой зависит от объекта-получателя. Более того, сам получатель может делегировать интерпретацию следующему объекту, обычно своему родителю. Это напоминает паттерн цепочка обязанностей. Таким образом, в Unidraw получатель вычисляется, а не хранится. Механизм интерпретации в Unidraw использует информацию о типе, доступную во время выполнения. оригинал codelab.ru codelab.ru источник
Джеймс Коплиен описывает, как в языке C++ реализуются функторы - объекты, ведущие себя, как функции. За счет перегрузки оператора вызова operator() он становится более понятным. Смысл паттерна команда в другом - он устанавливает и поддерживает связь между получателем и функцией (то есть действием), а не просто функцию. оригинал codelab.ru codelab.ru источник

  codelab.ru оригинал источник codelab.ru

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

Паттерн компоновщик можно использовать при реализации макрокоманд. codelab.ru источник оригинал codelab.ru
Паттерн хранитель иногда проектируется так, что сохраняет состояние команды, необходимое для отмены ее действия. источник codelab.ru оригинал codelab.ru

Команда, которую нужно копировать перед помещением в список истории, ведет себя, как прототип. codelab.ru codelab.ru оригинал источник



Реализации: java(5)   +добавить реализацию

1) Module.java, code #487[автор:this]
2) ImageGallery.java, code #488[автор:this]
3) SalesReports.java, code #489[автор:this]
4) MultiPage.java, code #490[автор:this]
5) InfoPage.java, code #491[автор:this]


<< назад наверх
распечатать обсудить >>

 
каталог | задачи | паттерны | исходники | стат | форумы | карта сайта | контакты | ссылки 
© 2000-2017 CodeLAB Group
  Все права защищены
Страница сгенерирована за 0.04463 секунд
Количество запросов к БД: 14, gzip: 21.5kb/101.9kb(79%)