Код на С#. Лаб11 ИИС. Отчет о лабораторной работе 10 Паттерны Адаптер, Команда. Исполнитель Яппарова М. Р. Группа пм119
Скачать 228.45 Kb.
|
Институт цифровых технологий и экономики Кафедра «ИК» Интегрированные информационные системы Отчет о лабораторной работе № 10 Паттерны: Адаптер, Команда. Исполнитель: Яппарова М.Р. Группа: ПМ-1-19 Дата выполнения 24 ноября 2022 г. Дата сдачи 24 ноября 2022 г. Оценка ____________ Подпись преподавателя _______________________ КАЗАНЬ - 2022 Паттерн Adapter Название – Адаптер. Также известен как Wrapper (обертка) Классификация По цели: структурный По применимости: к классам и объектам Частота использования Выше средней - 1 2 3 4 5 Назначение Паттерн Adapter - преобразует интерфейс (набор имен методов) одного класса в интерфейс (набор имен методов) другого класса, который ожидают клиенты. Адаптер обеспечивает совместную работу классов с несовместимыми интерфейсами, такая работа без Адаптера была бы невозможна. Общие сведения Класс Adapter приводит интерфейс класса Adaptee в соответствие с интерфейсом класса Target (который реализуется классом Adapter). Это позволяет объекту Client использовать объект Adaptee (посредством адаптера Adapter) так, словно он является экземпляром класса Target. Таким образом Client обращается к интерфейсу Target, реализованному классом Adapter, который перенаправляет обращение к Adaptee. Шаблон Адаптер позволяет в процессе проектирования не принимать во внимание возможные различия в интерфейсах уже существующих классов. Если есть класс, обладающий требуемыми методами и свойствами (по крайней мере, концептуально), то при необходимости всегда можно воспользоваться шаблоном Адаптер для приведения его интерфейса к нужному виду. Близким Адаптеру является шаблон Фасад, не всегда можно отличить один от другого. Структура паттерна на языке UML Структура паттерна на языке C# class Client { public void Request(Target target) { target.Request(); } } // класс, к которому надо адаптировать другой класс class Target { public virtual void Request() {} } // Адаптер class Adapter : Target { private Adaptee adaptee = new Adaptee(); public override void Request() { adaptee.SpecificRequest(); } } // Адаптируемый класс class Adaptee { public void SpecificRequest() {} } Пример программы, иллюстрирующей использование паттерна. class Program { static void Main(string[] args) { // путешественник Driver driver = new Driver(); // машина Auto auto = new Auto(); // отправляемся в путешествие driver.Travel(auto); // встретились пески, надо использовать верблюда Camel camel = new Camel(); // используем адаптер ITransport camelTransport = new CamelToTransportAdapter(camel); // продолжаем путь по пескам пустыни driver.Travel(camelTransport); Console.Read(); } } interface ITransport { void Drive(); } // класс машины class Auto : ITransport { public void Drive() { Console.WriteLine("Машина едет по дороге"); } } class Driver { public void Travel(ITransport transport) { transport.Drive(); } } // интерфейс животного interface IAnimal { void Move(); } // класс верблюда class Camel : IAnimal { public void Move() { Console.WriteLine("Верблюд идет по пескам пустыни"); } } // Адаптер от Camel к ITransport class CamelToTransportAdapter : ITransport { Camel camel; public CamelToTransportAdapter(Camel c) { camel = c; } public void Drive() { camel.Move(); } } Паттерн Command Название – Команда. Также известен как Action (Действие), Transaction (Транзакция) Классификация По цели: поведенческий По применимости: к объектам Частота использования Выше средней - 1 2 3 4 5 Назначение Паттерн Command – позволяет представить запрос в виде объекта, позволяя клиенту конфигурировать запрос (задавая параметры для его обработки), ставить запросы в очередь, протоколировать запросы, а также поддерживать отмену операций. Общие сведения В объектно-ориентированном программировании шаблон проектирования Команда является поведенческим шаблоном, в котором объект используется для инкапсуляции всей информации, необходимой для выполнения действия или вызова события в более позднее время. Эта информация включает в себя имя метода, объект, который является владельцем метода и значения параметров метода. Четыре термина всегда связаны с шаблоном Команда: команды (command), приёмник команд (receiver), вызывающий команды (invoker) и клиент (client). Объект Command знает о приёмнике и вызывает метод приемника. Значения параметров приёмника сохраняются в команде. Вызывающий объект (invoker) знает, как выполнить команду и, возможно, делает учёт и запись выполненных команд. Вызывающий объект (invoker) ничего не знает о конкретной команде, он знает только об интерфейсе. Оба объекта (вызывающий объект и несколько объектов команд) принадлежат объекту клиента (client). Клиент решает, какие команды выполнить и когда. Чтобы выполнить команду он передает объект команды вызывающему объекту (invoker). Использование командных объектов упрощает построение общих компонентов, которые необходимо делегировать или выполнять вызовы методов в любое время без необходимости знать методы класса или параметров метода. Использование вызывающего объекта (invoker) позволяет вести учёт выполненных команд без необходимости знать клиенту об этой модели учёта (такой учёт может пригодиться, например, для реализации отмены и повтора команд). Структура паттерна на языке UML Структура паттерна на языке C# abstract class Command { public abstract void Execute(); public abstract void Undo(); } // конкретная команда class ConcreteCommand : Command { Receiver receiver; public ConcreteCommand(Receiver r) { receiver = r; } public override void Execute() { receiver.Operation(); } public override void Undo() {} } // получатель команды class Receiver { public void Operation() { } } // инициатор команды class Invoker { Command command; public void SetCommand(Command c) { command = c; } public void Run() { command.Execute(); } public void Cancel() { command.Undo(); } } class Client { void Main() { Invoker invoker = new Invoker(); Receiver receiver = new Receiver(); ConcreteCommand command=new ConcreteCommand(receiver); invoker.SetCommand(command); invoker.Run(); } } Пример программы, иллюстрирующей использование паттерна class Program { static void Main(string[] args) { Pult pult = new Pult(); TV tv = new TV(); pult.SetCommand(new TVOnCommand(tv)); pult.PressButton(); pult.PressUndo(); Console.Read(); } } interface ICommand { void Execute(); void Undo(); } // Receiver - Получатель class TV { public void On() { Console.WriteLine("Телевизор включен!"); } public void Off() { Console.WriteLine("Телевизор выключен..."); } } class TVOnCommand : ICommand { TV tv; public TVOnCommand(TV tvSet) { tv = tvSet; } public void Execute() { tv.On(); } public void Undo() { tv.Off(); } } // Invoker - инициатор class Pult { ICommand command; public Pult() { } public void SetCommand(ICommand com) { command = com; } public void PressButton() { command.Execute(); } public void PressUndo() { command.Undo(); } } Итак, в этой программе есть интерфейс команды - ICommand, есть ее реализация в виде класса TVOnCommand, есть инициатор команды - класс Pult, некий прибор - пульт, управляющий телевизором. И есть получатель команды - класс TV, представляющий телевизор. В качестве клиента используется класс Program. При этом пульт ничего не знает об объекте TV. Он только знает, как отправить команду. В итоге мы получаем гибкую систему, в которой мы легко можем заменять одни команды на другие, создавать последовательности команд. Например, в нашей программе кроме телевизора появилась микроволновка, которой тоже неплохо было бы управлять с помощью одного интерфейса. Для этого достаточно добавить соответствующие классы и установить команду: class Program { static void Main(string[] args) { Pult pult = new Pult(); TV tv = new TV(); pult.SetCommand(new TVOnCommand(tv)); pult.PressButton(); pult.PressUndo(); Microwave microwave = new Microwave // 5000 - время нагрева пищи pult.SetCommand(new MicrowaveCommand(microwave, 5000)); pult.PressButton(); Console.Read(); } } //.....ранее описанные классы class Microwave { public void StartCooking(int time) { Console.WriteLine("Подогреваем еду"); // имитация работы с помощью асинхронного метода Task.Delay Task.Delay(time).GetAwaiter().GetResult(); } public void StopCooking() { Console.WriteLine("Еда подогрета!"); } } class MicrowaveCommand : ICommand { Microwave microwave; int time; public MicrowaveCommand(Microwave m, int t) { microwave = m; time = t; } public void Execute() { microwave.StartCooking(time); microwave.StopCooking(); } public void Undo() { microwave.StopCooking(); } } Теперь еще одним получателем запроса является класс Microwave, функциональностью которого можно управлять через команды MicrowaveCommand. Правда, в вышеописанной системе есть один изъян: если мы попытаемся выполнить команду до ее назначения, то программа выдаст исключение, так как команда будет не установлена. Эту проблему мы могли бы решить, проверяя команду на значение null в классе инициатора: class Pult { ICommand command; public Pult() { } public void SetCommand(ICommand com) { command = com; } public void PressButton() { if(command!=null) command.Execute(); } public void PressUndo() { if(command!=null) command.Undo(); } } Либо можно определить класс пустой команды, которая будет устанавливаться по умолчанию: class NoCommand : ICommand { public void Execute() { } public void Undo() { } } class Pult { ICommand command; public Pult() { command = new NoCommand(); } public void SetCommand(ICommand com) { command = com; } public void PressButton() { command.Execute(); } public void PressUndo() { command.Undo(); } } При этом инициатор необязательно указывает на одну команду. Он может управлять множеством команд. Например, на пульте от телевизора есть как кнопка для включения, так и кнопки для регулировки звука: class Program { static void Main(string[] args) { TV tv = new TV(); Volume volume = new Volume(); MultiPult mPult = new MultiPult(); mPult.SetCommand(0, new TVOnCommand(tv)); mPult.SetCommand(1, new VolumeCommand(volume)); // включаем телевизор mPult.PressButton(0); // увеличиваем громкость mPult.PressButton(1); mPult.PressButton(1); mPult.PressButton(1); // действия отмены mPult.PressUndoButton(); mPult.PressUndoButton(); mPult.PressUndoButton(); mPult.PressUndoButton(); Console.Read(); } } interface Command { void Execute(); void Undo(); } class TV { public void On() { Console.WriteLine("Телевизор включен!"); } public void Off() { Console.WriteLine("Телевизор выключен..."); } } class TVOnCommand : ICommand { TV tv; public TVOnCommand(TV tvSet) { tv = tvSet; } public void Execute() { tv.On(); } public void Undo() { tv.Off(); } } class Volume { public const int OFF = 0; public const int HIGH = 20; private int level; public Volume() { level = OFF; } public void RaiseLevel() { if (level < HIGH) level++; Console.WriteLine("Уровень звука {0}", level); } public void DropLevel() { if (level > OFF) level--; Console.WriteLine("Уровень звука {0}", level); } } class VolumeCommand : ICommand { Volume volume; public VolumeCommand(Volume v) { volume = v; } public void Execute() { volume.RaiseLevel(); } public void Undo() { volume.DropLevel(); } } class NoCommand : ICommand { public void Execute() { } public void Undo() { } } class MultiPult { ICommand[] buttons; Stack public MultiPult() { buttons = new ICommand[2]; for (int i = 0; i < buttons.Length; i++) { buttons[i] = new NoCommand(); } commandsHistory = new Stack } public void SetCommand(int number, ICommand com) { buttons[number] = com; } public void PressButton(int number) { buttons[number].Execute(); // добавляем выполненную команду в историю команд commandsHistory.Push(buttons[number]); } public void PressUndoButton() { if(commandsHistory.Count>0) { ICommand undoCommand = commandsHistory.Pop(); undoCommand.Undo(); } } } Здесь два получателя команд - классы TV и Volume. Volume управляет уровнем звука и сохраняет текущий уровень в переменной level. Также есть две команды TVOnCommand и VolumeCommand. Инициатор - MultiPult имеет две кнопки в виде массива buttons: первая предназначена для TV, а вторая - для увеличения уровня звука. Чтобы сохранить историю команд используется стек. При отправке команды в стек добавляется новый элемент, а при ее отмене, наоборот, происходит удаление из стека. В данном случае стек выполняет роль примитивного лога команд. Вывод: Мы изучили Паттерны: Адаптер, Команда. |