Видео курс по шаблонам проектирования. Command - онлайн обучение ITVDN
ITVDN: курсы программирования
Видеокурсы по
программированию

    Выбери свою IT специальность

    Начать бесплатно

    Выбери свою IT специальность

    Начать бесплатно

      Видео курс по шаблонам проектирования

      ×

      Вы открыли доступ к тесту! Пройти тест

      Вы действительно хотите открыть доступ к тестированию по курсу Шаблоны проектирования на 40 дней?

      ВИДЕОУРОК №20. Command

      Для просмотра полной версии видеокурса, онлайн тестирования и получения доступа к дополнительным учебным материалам купите курс Купить курс
      Для просмотра всех видеокурсов ITVDN, представленных в Каталоге, а также для получения доступа к учебным материалам и онлайн тестированию купите подписку Купить подписку
      В данном видео уроке рассматривается структура курса “Шаблоны проектирования” и производится краткий обзор книги “Приемы объектно-ориентированного проектирования. Паттерны проектирования” с обсуждением всех ее достоинств и недостатков. Также автор презентует книгу «Design Patterns via C#», которая в доходчивом, понятном и упрощенном виде объясняет назначение и применение паттернов в создании программного обеспечения.
      Читать дальше...
      Технически, паттерны (шаблоны) проектирования - это всего лишь абстрактные примеры правильного использования небольшого числа комбинаций простейших техник ООП. Видео урок представит простые примеры, показывающие правильные способы организации взаимодействий между классами или объектами.
      Читать дальше...
      Этот видео урок из "Курса шаблоны проектирования" посвящен UML (англ. Unified Modeling Language — унифицированный язык моделирования) — язык графического описания для объектного моделирования в области разработки программного обеспечения. UML является языком широкого профиля, это — открытый стандарт, использующий графические обозначения для создания абстрактной модели системы, называемой UML-моделью. Данный видеоурок познакомит вас c базовыми возможностями и подходами к проектированию с помощью языка UML.
      Читать дальше...
      Данный видео урок из курса "Шаблоны проектирования" познакомит Вас с понятием конечного автомата, вариантами его описания и логикой построения простейших конечных автоматов.
      Читать дальше...
      Видео урок раскроет понятие парадигмы программи́рования, которая является совокупностью идей и понятий, определяющих стиль написания компьютерных программ. Это способ концептуализации, определяющий организацию вычислений и структурирование работы, выполняемой компьютером. Этот видеоурок расскажет Вам об основных современных парадигмах объектно-ориентированного программирования, которые поддерживаются платформой .NET.
      Читать дальше...
      Данный видео урок дает базовые знания о регулярных грамматиках, и основных способах их применения в программировании.
      Читать дальше...
      Видео урок посвящен паттерну Abstract Factory, который предоставляет клиенту интерфейс (набор методов) для создания семейств взаимосвязанных или взаимозависимых объектов-продуктов, при этом скрывает от клиента информацию о конкретных классах этих объектов-продуктов.
      Читать дальше...
      Видеоурок посвящен паттерну проектирования Builder, который помогает организовать пошаговое построение сложного объекта-продукта так, что клиенту не требуется понимать последовательность шагов и внутреннее устройство строящегося объекта-продукта, при этом в результате одного и того же процесса конструирования могут получаться объекты-продукты с различным представлением (внутренним устройством).
      Читать дальше...
      Видео урок посвящен шаблону проектирования Factory Method, который предоставляет абстрактный интерфейс (набор методов) для создания объекта-продукта, но оставляет возможность, разработчикам классов, реализующих этот интерфейс самостоятельно принять решение о том, экземпляр какого конкретного класса-продукта создать. Паттерн Factory Method позволяет базовым абстрактным классам передать ответственность за создание объектов-продуктов своим производным классам.
      Читать дальше...
      Видеоурок расскажет о паттерне Prototype, который предоставляет возможность создания новых объектов-продуктов (клонов), используя технику клонирования (копирования) созданного ранее объекта-оригинала-продукта (прототипа). Паттерн Prototype – позволяет задать различные виды (классы-виды) объектов-продуктов (клонов), через настройку состояния каждого нового созданного клона. Классификация клонов-продуктов производится на основании различия их состояний.
      Читать дальше...
      Видео урок познакомит с шаблоном проектирования Singleton, который гарантирует, что у класса может быть только один экземпляр. В частном случае предоставляется возможность наличия, заранее определенного числа экземпляров.
      Читать дальше...
      Видео урок посвящен паттерну Adapter, он преобразует интерфейс (набор имен методов) одного класса в интерфейс (набор имен методов) другого класса, который ожидают клиенты. Адаптер обеспечивает совместную работу классов с несовместимыми интерфейсами, такая работа без Адаптера была бы невозможна.
      Читать дальше...
      Видео урок посвящен паттерну Bridge, который позволяет отделить абстракцию от элементов ее реализации так, чтобы и абстракцию, и реализацию можно было изменять независимо друг от друга.
      Читать дальше...
      Виде урок расскажет о шаблоне проектирования Composite, который составляет из объектов древовидные структуры для представления иерархий «часть – целое». Позволяет клиентам единообразно трактовать индивидуальные объекты (листья) и составные объекты (ветки).
      Читать дальше...
      Видео урок посвящен паттерну Decorator(декоратор), который динамически (в ходе выполнения программы) добавляет объекту новые возможности (состояние и/или поведение). Композиция, используемая при реализации паттерна Decorator, является гибкой альтернативой наследованию (порождению подклассов) с целью расширения функциональности.
      Читать дальше...
      Видео урок посвящен шаблону проектирования Facade, который предоставляет унифицированный интерфейс (набор имен методов) вместо интерфейса некоторой подсистемы (набора взаимосвязанных классов или объектов).
      Читать дальше...
      Видео урок посвящен шаблону проектированию Flyweight, который описывает правильное применение техники создания «разделяемых объектов», для получения возможности эффективного использования большого числа объектов.
      Читать дальше...
      Видео урок посвящен шаблону проектирования Proxy, который предоставляет объект-заместитель для контроля доступа к другому объекту.
      Читать дальше...
      Видео урок посвящен паттерну проектирования Chain of Responsibility, который позволяет избежать привязки объекта-отправителя запроса к объекту-получателю запроса, при этом давая шанс обработать этот запрос нескольким объектам. Паттерн Chain of Responsibility связывает в цепочку объекты-получатели запроса и передает запрос вдоль этой цепочки, пока один из объектов, составляющих эту цепочку не обработает передаваемый запрос.
      Читать дальше...
      Видео урок расскажет о шаблоне проектирования Command (Команда), который позволяет представить запрос в виде объекта, позволяя клиенту конфигурировать запрос (задавая параметры для его обработки), ставить запросы в очередь, протоколировать запросы, а также поддерживать отмену операций.
      Читать дальше...
      Видео урок посвящен шаблону проектирования Interpreter (интерпретатор), который позволяет сформировать объектно-ориентированное представление грамматики для заданного языка, а также описывает правила создания механизма интерпретации (толкования) предложений этого языка.
      Читать дальше...
      Видео урок посвящен шаблону проектирования Iterator (Итератор), который представляет удобный и безопасный способ доступа к элементам коллекции (составного объекта), при этом не раскрывая внутреннего представления этой коллекции.
      Читать дальше...
      Видео урок посвящен шаблону проектирования Mediator (посредник), который предоставляет объект-посредник, скрывающий способ взаимодействия множества других объектов-коллег. Объект-посредник обеспечивает слабую связанность системы, избавляя объектов-коллег от необходимости явно ссылаться друг на друга, позволяя тем самым независимо изменять взаимодействия между объектами-коллегами.
      Читать дальше...
      Видео урок посвящен поведенческому шаблоны проектирования Memento (хранитель), который не нарушая инкапсуляции, фиксирует и выносит за пределы объекта-хозяина его внутреннее состояние так, чтобы позднее это вынесенное состояние можно было восстановить в исходном объекте-хозяине.
      Читать дальше...
      Видео урок посвящен шаблону проектирования Observer (наблюдатель), который использует связь-отношения зависимости «один ко многим» (один издатель ко многим подписчикам). При изменении состояния одного объекта (издателя), все зависящие от него объекты (подписчики) оповещаются об этом и автоматически обновляются.
      Читать дальше...
      Видео урок расскажет о шаблоне проектирования State (Состояние), который позволяет объекту изменять свое поведение в зависимости от своего состояния. Поведение объекта изменяется на столько, что создается впечатление, что изменился класс объекта.
      Читать дальше...
      Видео урок посвящен шаблону проектирования Strategy (Стратегия), который определяет набор алгоритмов (часто схожих по роду деятельности), инкапсулирует каждый из имеющихся алгоритмов (в отдельный класс) и делает их подменяемыми. Паттерн Strategy позволяет подменять алгоритмы без участия клиентов, которые используют эти алгоритмы.
      Читать дальше...
      Видео урок посвящен паттерну проектирования Template Method, который формирует структуру алгоритма и позволяет в производных классах реализовать, заместить (перекрыть) или переопределить определенные шаги (участки) алгоритма, не изменяя структуру алгоритма в целом.
      Читать дальше...
      Видео урок посвящен шаблону проектирования Visitor (Посетитель), который позволяет единообразно обойти набор элементов с разнородными интерфейсами (т.е. набор объектов разных классов не приводя их к общему базовому типу), а также позволяет добавить новый метод (функцию) в класс объекта, при этом не изменяя сам класс этого объекта.
      Читать дальше...
      ПОКАЗАТЬ ВСЕ
      основные темы, рассматриваемые на уроке
      0:00:31
      Назначение паттерна (через метафору)
      0:04:12
      Пример (с диаграммой классов, и кодом C#)
      0:17:46
      Структура паттерна на языке UML
      0:19:27
      Структура паттерна на языке C#
      0:22:23
      Применимость Паттерна
      0:25:05
      Назначение паттерна
      0:28:38
      Использование паттерна
      ПОКАЗАТЬ ВСЕ
      Рекомендуемая литература
      Рекомендуемой литературы к данному уроку не предусмотрено
      Титры видеоурока

      Паттерн Command

      Название

      Команда

      Также известен как

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

      Классификация

      По цели: поведенческий

      По применимости: к объектам

      Частота использования

      Выше средней    -   1 2 3 4 5

      Назначение

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

      Введение

      В качестве примера системы, в которой возможно использовать паттерн Command предлагается рассмотреть упрощенный программный калькулятор с четырьмя арифметическими операциями Add, Sub, Mul, Div и двумя операциями Undo и Redo. Структурная схема калькулятора показана на рисунке ниже.

      В структуре калькулятора можно выделить UI (наборное поле с кнопками для цифр, знаков отмены и повтора операций, а также панель для отображения результата). В «корпусе» калькулятора расположены программные блоки, обеспечивающие реакцию калькулятора на поток внешних дискретных событий. Такими событиями являются нажатия кнопок наборного поля. 

      Состав программных блоков калькулятора, следующий:

      • УУ - Устройство управления (ControlUnit). Оно организует всю работу калькулятора, выдавая в подходящий момент элементарные объекты-команды типа: Add, Sub, Mul, Div, Undo, Redo. При этом УУ сохраняет историю использования команд, а также может отменять и восстанавливать ранее выполненные команды.
      • АУ - Арифметическое устройство (ArithmeticUnit). После получения «сигнала» (одной из четырех команд Add, Sub, Mul, Div) на вход выполняет арифметическую операцию.
      • Команды - Add, Sub, Mul, Div. Специальные объекты-команды, которые УУ использует для управления АУ. Каждый объект-команда связан с АУ и умеет правильно им управлять.

      Предлагается рассмотреть диаграмму классов на которой представлена модель калькулятора.

      См. пример к главе: \014_Command\002_Command_Undo_Redo

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

      См. пример к главе: \014_Command\002_Command_Undo_Redo

      Удобство такого построения калькулятора с использованием паттерна Command заключается в том, что оказывается легко добавлять новые команды, которые можно тонко конфигурировать. Также можно расширять вычислительные возможности самого калькулятора, например, логическим устройством или устройством для вычислений чисел с плавающей точкой (FPU). На рисунке ниже показана структурная схема калькулятора расширенного новой командой Pow и новым вычислительным устройством FPU.

      Структура паттерна на языке UML

      См. пример к главе: \014_Command\001_Command

      Структура паттерна на языке C#

      См. пример к главе: \014_Command\001_Command

      Участники

      • Command - Команда:

      Предоставляет интерфейс для выполнения операции.

      • ConcreteCommand - Конкретная команда:

      Представляет собой объектно-ориентированное выражение соответствия между объектом класса Receiver и выполняемым им действием Action. Реализует операцию Execute которая вызывает соответствующие операции объекта класса Receiver.

      • Client - Клиент:

      Создает объект класса ConcreteCommand и устанавливает его получателя - объект класса Receiver.

      • Invoker  - Инициатор:

      Обращается к объекту-команде для выполнения запроса.

      • Receiver - Получатель:

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

      Отношения между участниками

      Отношения между классами

      • Класс Invoker связан связью отношения агрегации с абстрактным классом Command.
      • Класс ConcreteCommand связан связью отношения наследования с абстрактным классом Command и связью отношения ассоциации с конкретным классом Receiver.

      Отношения между объектами

      • Клиент создает объект класса ConcreteCommand и устанавливает для него получателя (Receiver).
      • Объект класса Invoker сохраняет в списке историй команд объект класса ConcreteCommand и отправляет запрос на выполнение команды вызывая метод Execute на объекте-команде. Если требуется поддержка отмены (Undo) и повтора (Redo) операций, то объект ConcreteCommand перед выполнением тела метода Execute должен сохранить информацию о своем текущем состоянии, достаточную для выполнения отката (Undo).
      • Объект класса ConcreteCommand вызывает операции (Action) объекта-получателя (Receiver) для выполнения запроса.

      На диаграмме взаимодействий между объектами представленной ниже видно, как объект-команда (Command) разрывает связь-отношение между инициатором (Invoker) и получателем (Receiver). 

      Мотивация

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

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

      abstract class Command
      {
          public abstract void Execute();
      
          protected void LogExecution(string text)
          {
              MainForm.MainFormInstance.Log(this.GetType().Name + " -> " + text);
          }
      }
      

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

      С помощью объектов унаследованных от абстрактного класса Command легко реализовать меню, адаптируя абстрактный класс Command к имеющемуся в библиотеке .Net классу ToolStripMenuItem:

      class MyMenuItem : ToolStripMenuItem
      {
          public Command MenuCommand { get; set; }
      
          public MyMenuItem(string text, Command command)
              : base(text)
          {
              MenuCommand = command;
          }
      
          protected override void OnClick(EventArgs e)
          {
              base.OnClick(e);
              if (MenuCommand != null)
                  MenuCommand.Execute();
          }
      }
      

      Меню приложения будет состоять из объектов типа MyMenuItem, каждый из которых будет сконфигурирован экземпляром одного из конкретных подклассов класса Command: OpenCommand, CopyCommand, CutCommand, SelectAllTextCommand, PasteCommand, CloseCommand, - или набором из таких объектов типа MacroCommand, которые содержат ссылку на получателя запроса. При выборе некоторого пункта меню связанный с ним объект класса  MyMenuItem вызовет метод Execute, реализованный в классе инкапсулированного объекта-команды, который и выполнить необходимую операцию. Заметим, что объекты класса MyMenuItem, работают с интерфейсом заданным абстрактным классом Command и не содержат информацию о том, какую именно операцию они выполняют и каким именно объектом подкласса класса Command они оперируют.  Классы конкретных команд содержат информацию о получателе запросов – объекте типа Document, доступному через статическое свойство CurrentDocument класса MainForm, и вызывают одну или целый набор операций этого получателя.

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

      Применимость паттерна

      Паттерн Command рекомендуется использовать, когда:

      • необходимо параметризировать объекты-команды выполняемым действием, например, кнопки или элементы меню, а также callback методы, благодаря объектно-ориентированному представлению объектов-запросов. Заметим, что такая параметризация не является параметризацией типов в чистом виде, в языке С#  - она представлена обобщениями, поддерживаемыми платформой .Net, хотя в этом случае обобщения (generics), также могут быть использованы, например, при параметризации сложными  объектами классов Action<T> или Func<T, TResult >, где указатель на место заполнения типа T будет примать тип Command.

       

      • необходимо организовать разнесенное во времени добавление запросов в очередь и их выполнение. При этом срок жизни запроса в очереди и объекта-запроса класса, наследуемого от класса Command, могут быть независимыми и вообще существовать в различных процессах.

       

      • необходимо обеспечить отмену операций. Для этого нужно в интерфейсе абстрактного класса Command объявить метод Unxecute, вызов которого позволял бы осуществить откат действий, произведенных методом Execute. Возможно, для реализации такого действия, понадобиться предварительно сохранить необходимую информацию при выполнении метода Execute и сохранять последовательность действий Execute/Unxecute в списке истории внутри объекта класса, чтобы при необходимости можно было выполнить откат состояния объекта-получателя, выполняя обратные операции. Чтобы предоставить команде доступ к этой информации, не раскрывая внутреннее состояние объектов-носителей информации, можно воспользоваться паттерном Memento.

       

      • необходимо протоколировать (логировать) изменения. Такая возможность может понадобиться для отслеживания времени и инициатора изменений данных, а также для восстановления состояния  объектов после сбоя. В таком случае, необходимо дополнить интерфейс абстрактного класса Command методами сохранения и загрузки протокола изменений из внешнего источника. Тогда после сбоя можно будет загрузить протокол изменений и повторно выполнить последовательность операций при помощи методов Execute/Unxecute.

       

      • необходимо создать систему на основе транзакций т.е. с прозрачной структурой высокоуровневых операций на основе примитивных. Транзакция – объектно-ориентированное представление группы логически объединённых последовательных операций по работе с данными, обрабатываемое или отменяемое целиком. Паттерн Command позволяет проектировать системы с учетом транзакционного подхода благодаря наличию у всех низкоуровневых команд и высокоуровневых транзакций общего интерфейса, что позволяет легко добавлять в систему новые команды и организовывать на их основе новые транзакции, а также работать с ними единообразно.

       

      Результаты

      Паттерн Command:

      • отделяет объект инициатор операции от объекта, имеющего информацию о том, как ее выполнить.  Достигается благодаря адаптации (см. паттерн Adapter) абстрактного класса или интерфейса Command к классу объекта инициатора. При этом сам объект-инициатор может абсолютно не  владеть информацией об объекте-исполнителе и о сути выполняемой операции.

       

      • позволяет оперировать объектами при маршрутизации и обработке запросов. Конкретные классы команд инкапсулируют в себе адрес обработчика операции и высокоуровневую часть алгоритма обработки.

       

      • предоставляет возможность структурирования команд, путем компоновки сложных операций, например MacroCommand, из более простых составляющих.  При этом объект сложной операции имеет тот же интерфейс, что и все его составные части, т.е. реализует метод Execute базового абстрактного класса Command (см. паттерн Composite).

       

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

       

       

      Реализация

      Полезные приемы реализации паттерна Command:

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

       

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

       

      • адресе объекта-получателя класса Receiver, который выполняет операции по запросу;
      • аргументы и другие параметры используемые при вызове методов класса объекта-получателя;
      • исходном состоянии объекта-получателя Receiver, т.е. значения его полей, которые могли измениться в результате выполнения команды.

       

      Также необходимым условием является предоставление доступа объектом Receiver к методам, позволяющим команде вернуть этот объект в исходное состояние. Обратим внимание, что если в результате выполнения команды меняется состояние не только объекта-получателя, но и самой команды, то объект команды также необходимо хранить в истории изменений, копируя его после выполнения, так как он может быть изменен при следующем вызове, и тогда выполнить откат действий корректно не удастся. Если в результате выполнения команды ее состояние не изменяется (не изменяются атрибуты объекта класса ConcreteCommand), то и хранить копию объекта не обязательно, достаточно хранения ссылки на объект-команду. Команды, которые изменяются при выполнении и нуждаются в хранении копий-клонов в списке истории ведут себя подобно прототипам (см. паттерн Prototype).

       

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

       

      • применение шаблонов. Для однотипных команд, которые не поддерживают операции отмены/повтора (Undo/Redo) можно воспользоваться подходом создания команды-шаблона – объекта типа SimpleCommand : Command на основе делегатов типов Action, Action<T>, Func<T, TResult>, или delegate, где метод Execute, будет вызывать необходимые callback методы. Такой подход позволяет сократить количество подклассов класса Command. Подробнее использование шаблонных команд рассматривается в примере ниже.

       

      Пример кода

      Рассмотрим детальнее конкретные классы команд из раздела Мотивация. Класс OpenCommand, реализует команду открытия документа выбранного пользователем в приложении, используя метод AddDocument класса MainForm и реализуя абстрактный метод Execute родительского абстрактного класса Command:

          class OpenCommand : Command
          {
              public override void Execute()
              {
                  var filename = AskUser();
                  if (!string.IsNullOrEmpty(filename))
                  {
                      var doc = new Document();
                      doc.Open(filename);
                      LogExecution(" opened");
                      MainForm.MainFormInstance.AddDocument(doc);
                  }
                  else
                  { LogExecution(" opening cancelled"); }
              }
              string AskUser()
              {
                  LogExecution("Asking user.");
                  var dlg = new OpenFileDialog();
                  dlg.InitialDirectory = Application.StartupPath;
                  if (dlg.ShowDialog() == DialogResult.OK)
                  {
                      return dlg.FileName;
                  }
                  else
                      return string.Empty;
              }
          }
      
          public partial class MainForm : Form
          {
              internal static Document CurrentDocument { get; set; }
              internal static MainForm MainFormInstance { get; private set; }
      
              public MainForm()
              {
                  InitializeComponent();
                  toolStripMenuItem1.DropDownItems.AddRange(new ToolStripItem[] {
                      new MyMenuItem("Open",new OpenCommand()),
                      new MyMenuItem("Close",new CloseCommand()),
                      new ToolStripSeparator(),
                      new MyMenuItem("Cut",new CutCommand()),
                      new MyMenuItem("Copy",new CopyCommand()),
                      new MyMenuItem("Paste",new PasteCommand()),
                      new ToolStripSeparator(),
                      new MyMenuItem("MacroCopy",new MacroCommand(new OpenCommand(),
                                                                  new SelectAllTextCommand(),
                                                                  new CopyCommand(),
                                                                  new CloseCommand()))
              });
                  MainFormInstance = this;
              }
      
      
              private void AddMenuItemWithTemplateCommands()
              {
                  toolStripMenuItem2 = new System.Windows.Forms.ToolStripMenuItem();
      
                  this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
                  toolStripMenuItem2});
      
                  toolStripMenuItem2.Name = "toolStripMenuItem2";
                  toolStripMenuItem2.Size = new System.Drawing.Size(106, 20);
                  toolStripMenuItem2.Text = "DocumentTemplate Menu";
      
                  toolStripMenuItem2.DropDownItems.AddRange(new ToolStripItem[] {
                      new MyMenuItem("Cut",new SimpleCommand(CurrentDocument.Cut,"cut")),
                      new MyMenuItem("Copy",new SimpleCommand(CurrentDocument.Copy, "copy")),
                      new MyMenuItem("Paste",new SimpleCommand(CurrentDocument.Paste,"paste")),
                  });
              }
      
       public void AddDocument(Document document)
              {
                  document.MdiParent = this;
                  document.Show();
      
                  AddMenuItemWithTemplateCommands();
              }
      
              public void Log(string logString)
              {
                  logLabel.Text += Environment.NewLine + logString;
              }
          }
      

      Классы CloseCommand, CopyCommand, CutCommand, PasteCommand, SelectAllTextCommand и MacroCommand аналогично реализуют соответственно закрытие документа, копирование и вырезание выделенного фрагмента текста, вставку текста из буфера обмена, выделение всего текстового содержимого в документе и заданную в коде приложения последовательность команд.

          class CloseCommand : Command
          {
              public override void Execute()
              {
                  if (MainForm.CurrentDocument != null)
                  {
                      LogExecution("close");
                      MainForm.CurrentDocument.Close();
                  }
              }
          }
      
          class CopyCommand : Command
          {
              public override void Execute()
              {
                  if (MainForm.CurrentDocument != null)
                  {
                      LogExecution("copy text: " + MainForm.CurrentDocument.DocumentContent.SelectedText);  MainForm.CurrentDocument.Copy();
                  }
              }
          }
      
          class CutCommand : Command
          {
              public override void Execute()
              {
                  if (MainForm.CurrentDocument != null)
                  {
                      LogExecution("cut text: " +      MainForm.CurrentDocument.DocumentContent.SelectedText);
                      MainForm.CurrentDocument.Cut();
                  }
              }
          }
      
          class PasteCommand : Command
          {
      
              public override void Execute()
              {
      
                  if (MainForm.CurrentDocument != null)
                  {
                      LogExecution("paste text: " + Clipboard.GetText());
                      MainForm.CurrentDocument.Paste();
                  }
              }
          }
      
          class SelectAllTextCommand : Command
          {
              public override void Execute()
              {
                  if (MainForm.CurrentDocument != null)
                  {
                      LogExecution(MainForm.CurrentDocument.Text + "select all text");
                      MainForm.CurrentDocument.DocumentContent.SelectAll();
                  }
              }
          }
      
          class MacroCommand : Command
          {
              public readonly List<Command> Commands = new List<Command>();
      
              public MacroCommand(params Command[] commands)
              {
                  Commands.AddRange(commands);
              }
      
              public override void Execute()
              {
                  foreach (var c in Commands)
                      c.Execute();
              }
          }
      

      Заметим, что класс MacroCommand не содержит ссылок на объекты-исполнители запросов, т.к. они инкапсулированы в объектах типа Command, набор которых хранит в себе данный класс.

          class SimpleCommand : Command
          {
              Action action;
              string actionKeyword;
      
              public SimpleCommand(Action action, string actionKeyword)
              {
                  this.action = action;
                  this.actionKeyword = actionKeyword;
              }
      
              public override void Execute()
              {
                  if (action != null)
                  {
      LogExecution(actionKeyword + " text: " +   MainForm.CurrentDocument.DocumentContent.SelectedText);
                   action.Invoke();
                  }
              }
          }
      

      В классе SimpleCommand каждая связка «объект-исполнитель – действие» задается делегатом типа Action при вызове конструктора класса, а объекты класса SimpleCommand оперируют только ссылкой на необходимый callback метод, который передан с делегатом класса Action.

       

       

      Известные применения паттерна в .Net

      System.Data.Odbc.OdbcCommand

      http://msdn.microsoft.com/ru-ru/library/system.data.odbc.odbccommand(v=vs.110).aspx

      System.Data.OleDb.OleDbCommand

      http://msdn.microsoft.com/ru-ru/library/system.data.oledb.oledbcommand(v=vs.110).aspx

      System.Data.OracleClient.OracleCommand

      http://msdn.microsoft.com/en-us/library/system.data.oracleclient.oraclecommand(v=vs.110).aspx

      System.Data.SqlClient.SqlCommand

      http://msdn.microsoft.com/ru-ru/library/system.data.sqlclient.sqlcommand(v=vs.100).aspx

      System.Windows.Input.ICommand

      http://msdn.microsoft.com/en-us/library/system.windows.input.icommand(v=vs.110).aspx

      System.Windows.Input.ComponentCommands

      http://msdn.microsoft.com/ru-ru/library/system.windows.input.componentcommands(v=vs.110).aspx

      System.Windows.Input.MediaCommands

      http://msdn.microsoft.com/ru-ru/library/system.windows.input.mediacommands(v=vs.110).aspx

      System.Windows.Input.NavigationCommands

      http://msdn.microsoft.com/ru-ru/library/system.windows.input.navigationcommands(v=vs.110).aspx

      System.Windows.Input.RoutedCommand

      http://msdn.microsoft.com/ru-ru/library/system.windows.input.routedcommand(v=vs.110).aspx

      System.Windows.SystemCommands

      http://msdn.microsoft.com/ru-ru/library/system.windows.systemcommands(v=vs.110).aspx

      System.Workflow.ComponentModel.Design.WorkflowMenuCommands

      http://msdn.microsoft.com/ru-ru/library/system.workflow.componentmodel.design.workflowmenucommands(v=vs.110).aspx

      Пакеты подписки с доступом ко всем курсам и сервисам

      Пакеты подписки с доступом ко всем курсам и сервисам

      Стартовый
      • Все видеокурсы на 3 месяца
      • Тестирование по 10 курсам
      • Проверка 5 домашних заданий
      • Консультация с тренером 30 мин
      49.99 $
      25.00 $
      Подписка
      Базовый
      • Все видеокурсы на 6 месяцев
      • Тестирование по 16 курсам
      • Проверка 10 домашних заданий
      • Консультация с тренером 60 мин
      89.99 $
      45.00 $
      Подписка
      Премиум
      • Все видеокурсы на 12 месяцев
      • Тестирование по 24 курсам
      • Проверка 20 домашних заданий
      • Консультация с тренером 120 мин
      169.99 $
      85.00 $
      Подписка
      комментарии и обсуждения
      Notification success
      Мы используем cookie-файлы, чтобы сделать взаимодействие с нашими веб-сайтами и услугами простым и значимым.