Шаблон команды - Command pattern

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

С шаблоном команды всегда связаны четыре термина: команда, получатель, призыватель и клиент. А команда объект знает о получатель и вызывает метод получателя. Значения параметров метода приемника хранятся в команде. Объект-получатель для выполнения этих методов также сохраняется в объекте команды с помощью агрегирование. В получатель затем выполняет работу, когда выполнять() метод в команда называется. An призыватель объект знает, как выполнить команду, и, при необходимости, ведет учет выполнения команды. Вызывающий ничего не знает о конкретной команде, он знает только о команде интерфейс. Объект (ы) Invoker, объекты команд и объекты приемника удерживаются клиент объект, клиент решает, какие объекты-получатели он назначает объектам команд, а какие команды - вызывающему. Клиент решает, какие команды выполнять в каких точках. Чтобы выполнить команду, он передает объект команды вызывающему объекту.

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

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

Обзор

Команда[1]шаблон дизайна - один из двадцати трех хорошо известных Шаблоны проектирования GoF в которых описывается, как решать повторяющиеся проблемы проектирования для разработки гибкого и многократно используемого объектно-ориентированного программного обеспечения, то есть объектов, которые легче реализовать, изменить, протестировать и повторно использовать.

Использование шаблона проектирования Command может решить следующие проблемы:[2]

  • Следует избегать привязки инициатора запроса к конкретному запросу. То есть следует избегать жестких запросов.
  • Должна быть возможность настроить объект (который вызывает запрос) с запросом.

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

Использование шаблона проектирования Command описывает следующее решение:

  • Определите отдельные (командные) объекты, которые инкапсулируют запрос.
  • Класс делегирует запрос командному объекту вместо того, чтобы напрямую реализовывать конкретный запрос.

Это позволяет сконфигурировать класс с командным объектом, который используется для выполнения запроса. Класс больше не связан с конкретным запросом и не знает (не зависит) от того, как этот запрос выполняется.

См. Также схему классов и последовательности UML ниже.

Структура

Схема классов и последовательности UML

Пример класса UML и диаграмма последовательности для шаблона проектирования Command. [3]

В приведенном выше UML диаграмма классов, то Invoker класс не реализует запрос напрямую. Invoker относится к Команда интерфейс для выполнения запроса (command.execute ()), что делает Invoker независимо от того, как выполняется запрос. Command1 класс реализует Команда интерфейс, выполнив действие на приемнике (Receiver1.action1 ()).

В UML схема последовательности показывает взаимодействия во время выполнения: Invoker вызовы объектов выполнять() на Command1 объект.Command1 звонки действие1 () на Приемник1 объект, выполняющий запрос.

Диаграмма классов UML

UML-диаграмма шаблона команды

Использует

Кнопки и пункты меню графического интерфейса
В Качели и Borland Delphi программирование, Действие это командный объект. Помимо умения выполнять желаемую команду, Действие может иметь связанный значок, сочетание клавиш, текст всплывающей подсказки и т. д. Компонент кнопки панели инструментов или элемента меню можно полностью инициализировать, используя только Действие объект.
Макрос запись
Если все действия пользователя представлены объектами команд, программа может записывать последовательность действий, просто сохраняя список объектов команд по мере их выполнения. Затем он может «воспроизвести» те же действия, снова последовательно выполняя те же объекты команд. Если в программу встроен механизм сценариев, каждый объект команды может реализовать toScript () метод, и действия пользователя могут быть легко записаны в виде сценариев.
Мобильный код
Используя такие языки, как Java, где код может передаваться / передаваться из одного места в другое с помощью загрузчиков URLClassloaders и кодовых баз, команды могут обеспечивать доставку нового поведения в удаленные места (EJB Command, Master Worker).
Многоуровневый отменить
Если все действия пользователя в программе реализованы как объекты команд, программа может хранить стек последних выполненных команд. Когда пользователь хочет отменить команду, программа просто выводит самый последний объект команды и выполняет его. отменить() метод.
Сети
Можно отправлять целые объекты команд по сети для выполнения на других машинах, например, действия игрока в компьютерных играх.
Параллельная обработка
Где команды записываются как задачи для общего ресурса и выполняются многими потоками параллельно (возможно, на удаленных машинах; этот вариант часто называют шаблоном Master / Worker)
Индикаторы прогресса
Предположим, у программы есть последовательность команд, которые она выполняет по порядку. Если каждый командный объект имеет getEstimatedDuration () метод, программа может легко оценить общую продолжительность. Он может отображать индикатор выполнения, который значимо отражает, насколько близка программа к выполнению всех задач.
Пулы потоков
Типичный класс пула потоков общего назначения может иметь общедоступный addTask () метод, который добавляет рабочий элемент во внутреннюю очередь задач, ожидающих выполнения. Он поддерживает пул потоков, которые выполняют команды из очереди. Элементы в очереди - это командные объекты. Обычно эти объекты реализуют общий интерфейс, такой как java.lang.Runnable это позволяет пулу потоков выполнять команду, даже если сам класс пула потоков был написан без каких-либо сведений о конкретных задачах, для которых он будет использоваться.
Транзакционный поведение
Как и в случае отмены, ядро ​​базы данных или установщик программного обеспечения может хранить список операций, которые были или будут выполнены. Если один из них терпит неудачу, все остальные могут быть отменены или отброшены (обычно это называется откат). Например, если две таблицы базы данных, которые ссылаются друг на друга, должны быть обновлены, а второе обновление не удается, транзакцию можно откатить, чтобы первая таблица теперь не содержала недопустимую ссылку.
Мастера
Часто мастер представляет несколько страниц конфигурации для одного действия, которое происходит только тогда, когда пользователь нажимает кнопку «Готово» на последней странице. В этих случаях естественный способ отделить код пользовательского интерфейса от кода приложения - реализовать мастер с помощью объекта команды. Командный объект создается при первом запуске мастера. Каждая страница мастера сохраняет изменения своего графического интерфейса в объекте команды, поэтому объект заполняется по мере продвижения пользователя. «Готово» просто вызывает звонок выполнять(). Таким образом, командный класс будет работать.

Терминология

Терминология, используемая для описания реализаций шаблонов команд, непоследовательна и поэтому может сбивать с толку. двусмысленность, использование синонимы, и реализации, которые могут скрыть исходный шаблон, выходя далеко за его рамки.

  1. Двусмысленность.
    1. Период, термин команда неоднозначно. Например, двигаться вверх, двигаться вверх может относиться к одной команде (перемещаться вверх), которая должна выполняться дважды, или к двум командам, каждая из которых выполняет одно и то же (перемещение вверх). Если первая команда добавляется дважды в стек отмены, оба элемента в стеке относятся к одному и тому же экземпляру команды. Это может быть целесообразно, если команду всегда можно отменить одним и тем же способом (например, переместиться вниз). Оба Банда четырех и Пример Java ниже используйте эту интерпретацию термина команда. С другой стороны, если последние команды добавляются в стек отмены, стек ссылается на два отдельных объекта. Это может быть целесообразно, когда каждый объект в стеке должен содержать информацию, позволяющую отменить команду. Например, чтобы отменить удалить выделение , объект может содержать копию удаленного текста, чтобы его можно было повторно вставить, если удалить выделение команда должна быть отменена. Обратите внимание, что использование отдельного объекта для каждого вызова команды также является примером схема цепочки ответственности.
    2. Период, термин выполнять тоже неоднозначно. Это может относиться к запуску кода, идентифицированного командным объектом выполнять метод. Однако в Microsoft Windows Presentation Foundation команда считается выполненной, если команда выполнять был вызван, но это не обязательно означает, что код приложения запущен. Это происходит только после некоторой дальнейшей обработки событий.
  2. Синонимы и омонимы.
    1. Клиент, источник, инициатор: нажатие кнопки, кнопки панели инструментов или пункта меню; нажатие пользователем сочетания клавиш.
    2. Командный объект, направленный командный объект, объект действия: одноэлементный объект (например, есть только один объект CopyCommand), который знает о сочетаниях клавиш, изображениях кнопок, тексте команды и т. д., связанных с командой. Объект-источник / вызывающий объект вызывает метод execute / performAction объекта Command / Action. Объект Command / Action уведомляет соответствующие объекты-источники / вызывающие объекты, когда доступность команды / действия изменилась. Это позволяет кнопкам и пунктам меню становиться неактивными (неактивными), когда команда / действие не может быть выполнено / выполнено.
    3. Приемник, целевой объект: объект, который будет скопирован, вставлен, перемещен и т. д. Объект-получатель владеет методом, который вызывается командой выполнять метод. Приемник обычно также является целевым объектом. Например, если объект-получатель является курсор и метод называется двигаться вверх, то можно было бы ожидать, что курсор является целью действия moveUp. С другой стороны, если код определяется самим объектом команды, целевой объект будет совершенно другим объектом.
    4. Командный объект, аргументы перенаправленного события, объект события: объект, который передается от источника к объекту Command / Action, к объекту Target к коду, который выполняет работу. Каждое нажатие кнопки или сочетания клавиш приводит к созданию нового объекта команды / события. Некоторые реализации добавляют дополнительную информацию к объекту команды / события, поскольку он передается от одного объекта (например, CopyCommand) к другому (например, раздел документа). Другие реализации помещают объекты команд / событий в другие объекты событий (например, прямоугольник внутри большего прямоугольника) по мере их перемещения по линии, чтобы избежать конфликтов имен. (Смотрите также схема цепочки ответственности.)
    5. Обработчик, ExecutedRoutedEventHandler, метод, функция: фактический код, который выполняет копирование, вставку, перемещение и т.д. В некоторых реализациях код обработчика является частью объекта команды / действия. В других реализациях код является частью объекта Receiver / Target, а в других реализациях код обработчика хранится отдельно от других объектов.
    6. Диспетчер команд, Диспетчер отмены, Планировщик, Очередь, Диспетчер, Вызов: объект, который помещает объекты команд / событий в стек отмены или стек повтора, или который удерживает объекты команд / событий, пока другие объекты не будут готовы действовать на них, или который направляет объекты команд / событий к соответствующему получателю / цели код объекта или обработчика.
  3. Реализации, выходящие далеко за рамки исходного шаблона команд.
    1. Microsoft Windows Presentation Foundation (WPF) представляет перенаправленные команды, сочетающие шаблон команды с обработкой событий. В результате командный объект больше не содержит ни ссылки на целевой объект, ни ссылки на код приложения. Вместо этого вызов командного объекта выполнять команда приводит к так называемому Выполненное перенаправленное событие что во время туннелирования или всплытия события может возникнуть так называемый привязка объект, который идентифицирует цель и код приложения, который выполняется в этой точке.

пример

Рассмотрим «простой» переключатель. В этом примере мы настраиваем Switch с помощью двух команд: включить свет и выключить свет.

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

с помощью Система;пространство имен CommandPattern{        общественный интерфейс ICommand    {        пустота Выполнить();    }    / * Класс Invoker * /    общественный класс Переключатель    {        ICommand _closedCommand;        ICommand _openedCommand;        общественный Переключатель(ICommand closedCommand, ICommand openCommand)        {            этот._closedCommand = closedCommand;            этот._openedCommand = openCommand;        }        // Замыкаем цепь / включаем        общественный пустота близко()        {           этот._closedCommand.Выполнить();        }        // Обрыв цепи / отключение питания        общественный пустота Открыто()        {            этот._openedCommand.Выполнить();        }    }    / * Интерфейс, определяющий действия, которые может выполнять получатель * /    общественный интерфейс Переключаемый    {        пустота Включить();        пустота Выключение();    }    / * Класс Receiver * /    общественный класс Свет : Переключаемый    {        общественный пустота Включить()        {            Консоль.WriteLine("Свет горит");        }        общественный пустота Выключение()        {            Консоль.WriteLine("Свет выключен");        }    }    / * Команда выключения устройства - ConcreteCommand # 1 * /    общественный класс CloseSwitchCommand : ICommand    {        частный Переключаемый _switchable;        общественный CloseSwitchCommand(Переключаемый переключаемый)        {            _switchable = переключаемый;        }        общественный пустота Выполнить()        {            _switchable.Выключение();        }    }    / * Команда включения устройства - ConcreteCommand # 2 * /    общественный класс OpenSwitchCommand : ICommand    {        частный Переключаемый _switchable;        общественный OpenSwitchCommand(Переключаемый переключаемый)        {            _switchable = переключаемый;        }        общественный пустота Выполнить()        {            _switchable.Включить();        }    }    / * Тестовый класс или клиент * /    внутренний класс Программа    {        общественный статический пустота Основной(строка[] аргументы)        {            строка аргумент = аргументы.Длина > 0 ? аргументы[0].ToUpper() : значение NULL;            Переключаемый лампа = новый Свет();            // Передаем ссылку на экземпляр лампы каждой команде            ICommand переключательЗакрыть = новый CloseSwitchCommand(лампа);            ICommand switchOpen = новый OpenSwitchCommand(лампа);            // Передаем ссылку на экземпляры объектов Command переключателю            Переключатель @выключатель = новый Переключатель(переключательЗакрыть, switchOpen);            если (аргумент == "НА")            {                // Коммутатор (Invoker) вызовет Execute () для объекта команды.                @выключатель.Открыто();            }            еще если (аргумент == "ВЫКЛЮЧЕННЫЙ")            {                // Переключатель (Invoker) вызовет Execute () для объекта команды.                @выключатель.близко();            }            еще            {                Консоль.WriteLine("Аргумент " ВКЛ  "или " ВЫКЛ  "обязателен».);            }        }    }}


Смотрите также

использованная литература

  1. ^ Эрих Гамма, Ричард Хелм, Ральф Джонсон, Джон Влиссидес (1994). Паттерны проектирования: элементы объектно-ориентированного программного обеспечения многократного использования. Эддисон Уэсли. стр.233ff. ISBN  0-201-63361-2.CS1 maint: несколько имен: список авторов (ссылка на сайт)
  2. ^ «Шаблон проектирования Command - проблема, решение и применимость». w3sDesign.com. Получено 2017-08-12.
  3. ^ «Шаблон проектирования Command - структура и взаимодействие». w3sDesign.com. Получено 2017-08-12.

внешние ссылки