Методические рекомендации по выполнению практических работ по междисциплинарному курсу
Скачать 2.6 Mb.
|
Фасад (Facade) Фасад (Facade) представляет шаблон проектирования, который позволяет скрыть сложность системы с помощью предоставления упрощенного интерфейса для взаимодействия с ней. Когда использовать фасад? Когда имеется сложная система, и необходимо упростить с ней работу. Фасад позволит определить одну точку взаимодействия между клиентом и системой. Когда надо уменьшить количество зависимостей между клиентом и сложной системой. Фасадные объекты позволяют отделить, изолировать компоненты системы от клиента и развивать и работать с ними независимо. Когда нужно определить подсистемы компонентов в сложной системе. Создание фасадов для компонентов каждой отдельной подсистемы позволит упростить взаимодействие между ними и повысить их независимость друг от друга. В UML общую схему фасада можно представить следующим образом: Формальное определение программы в C# может выглядеть так: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class SubsystemA { public void A1() {} } class SubsystemB { public void B1() {} } class SubsystemC { public void C1() {} } public class Facade { SubsystemA subsystemA; SubsystemB subsystemB; SubsystemC subsystemC; 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 public Facade(SubsystemA sa, SubsystemB sb, SubsystemC sc) { subsystemA = sa; subsystemB = sb; subsystemC = sc; } public void Operation1() { subsystemA.A1(); subsystemB.B1(); subsystemC.C1(); } public void Operation2() { subsystemB.B1(); subsystemC.C1(); } } class Client { public void Main() { Facade facade = new Facade(new SubsystemA(), new SubsystemB(), new SubsystemC()); facade.Operation1(); facade.Operation2(); } } Участники Классы SubsystemA, SubsystemB, SubsystemC и т.д. являются компонентами сложной подсистемы, с которыми должен взаимодействовать клиент Client взаимодействует с компонентами подсистемы Facade - непосредственно фасад, который предоставляет интерфейс клиенту для работы с компонентами Рассмотрим применение паттерна в реальной задаче. Думаю, большинство программистов согласятся со мной, что писать в Visual Studio код одно удовольствие по сравнению с тем, как писался код ранее до появления интегрированных сред разработки. Мы просто пишем код, нажимаем на кнопку и все - приложение готово. В данном случае интегрированная среда разработки представляет собой фасад, который скрывает всю сложность процесса компиляции и запуска приложения. Теперь опишем этот фасад в программе на C#: 1 2 3 4 5 6 7 8 9 10 11 class Program { static void Main(string[] args) { TextEditor textEditor = new TextEditor(); Compiller compiller = new Compiller(); CLR clr = new CLR(); VisualStudioFacade ide = new VisualStudioFacade(textEditor, compiller, clr); Programmer programmer = new Programmer(); 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 programmer.CreateApplication(ide); Console.Read(); } } // текстовый редактор class TextEditor { public void CreateCode() { Console.WriteLine("Написание кода"); } public void Save() { Console.WriteLine("Сохранение кода"); } } class Compiller { public void Compile() { Console.WriteLine("Компиляция приложения"); } } class CLR { public void Execute() { Console.WriteLine("Выполнение приложения"); } public void Finish() { Console.WriteLine("Завершение работы приложения"); } } class VisualStudioFacade { TextEditor textEditor; Compiller compiller; CLR clr; public VisualStudioFacade(TextEditor te, Compiller compil, CLR clr) { this.textEditor = te; this.compiller = compil; this.clr = clr; } public void Start() { textEditor.CreateCode(); textEditor.Save(); compiller.Compile(); 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 clr.Execute(); } public void Stop() { clr.Finish(); } } class Programmer { public void CreateApplication(VisualStudioFacade facade) { facade.Start(); facade.Stop(); } } В данном случае компонентами системы являются класс текстового редактора TextEditor, класс компилятора Compiller и класс общеязыковой среды выполнения CLR. Клиентом выступает класс программиста, фасадом - класс VisualStudioFacade, который через свои методы делегирует выполнение работы компонентам и их методам. При этом надо учитывать, что клиент может при необходимости обращаться напрямую к компонентам, например, отдельно от других компонентов использовать текстовый редактор. Но в виду сложности процесса создания приложения лучше использовать фасад. Также это не единственный возможный фасад для работы с данными компонентами. При необходимости можно создавать альтернативные фасады также, как в реальной жизни мы можем использовать альтернативные среды разработки. Практическая работа №18 Использование поведенческих шаблонов Паттерны поведения Стратегия (Strategy) обеспечивает их взаимозаменяемость. В зависимости от ситуации мы можем легко заменить один используемый алгоритм другим. При этом замена алгоритма происходит независимо от объекта, который использует данный алгоритм. Когда использовать стратегию? Когда есть несколько родственных классов, которые отличаются поведением. Можно задать один основной класс, а разные варианты поведения вынести в отдельные классы и при необходимости их применять Когда необходимо обеспечить выбор из нескольких вариантов алгоритмов, которые можно легко менять в зависимости от условий Когда необходимо менять поведение объектов на стадии выполнения программы Когда класс, применяющий определенную функциональность, ничего не должен знать о ее реализации Формально паттерн Стратегия можно выразить следующей схемой UML: Формальное определение паттерна на языке C# может выглядеть следующим образом: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public interface IStrategy { void Algorithm(); } public class ConcreteStrategy1 : IStrategy { public void Algorithm() {} } public class ConcreteStrategy2 : IStrategy { public void Algorithm() {} } public class Context { public IStrategy ContextStrategy { get; set; } public Context(IStrategy _strategy) { ContextStrategy = _strategy; } public void ExecuteAlgorithm() { ContextStrategy.Algorithm(); } } Участники Как видно из диаграммы, здесь есть следующие участники: Интерфейс IStrategy, который определяет метод Algorithm(). Это общий интерфейс для всех реализующих его алгоритмов. Вместо интерфейса здесь также можно было бы использовать абстрактный класс. Классы ConcreteStrategy1 и ConcreteStrategy, которые реализуют интерфейс IStrategy, предоставляя свою версию метода Algorithm(). Подобных классов-реализаций может быть множество. Класс Context хранит ссылку на объект IStrategy и связан с интерфейсом IStrategy отношением агрегации. В данном случае объект IStrategy заключена в свойстве ContextStrategy, хотя также для нее можно было бы определить приватную переменную, а для динамической установки использовать специальный метод. Теперь рассмотрим конкретный пример. Существуют различные легковые машины, которые используют разные источники энергии: электричество, бензин, газ и так далее. Есть гибридные автомобили. В целом они похожи и отличаются преимущественно видом источника энергии. Не говоря уже о том, что мы можем изменить применяемый источник энергии, модифицировав автомобиль. И в данном случае вполне можно применить паттерн стратегию: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 class Program { static void Main(string[] args) { Car auto = new Car(4, "Volvo", new PetrolMove()); auto.Move(); auto.Movable = new ElectricMove(); auto.Move(); Console.ReadLine(); } } interface IMovable { void Move(); } class PetrolMove : IMovable { public void Move() { Console.WriteLine("Перемещение на бензине"); } } class ElectricMove : IMovable { public void Move() { Console.WriteLine("Перемещение на электричестве"); } } class Car { protected int passengers; // кол-во пассажиров 36 37 38 39 40 41 42 43 44 45 46 47 48 49 protected string model; // модель автомобиля public Car(int num, string model, IMovable mov) { this.passengers = num; this.model = model; Movable = mov; } public IMovable Movable { private get; set; } public void Move() { Movable.Move(); } } В данном случае в качестве IStrategy выступает интерфейс IMovable, определяющий метод Move(). А реализующий этот интерфейс семейство алгоритмов представлено классами ElectricMove и PetroleMove. И данные алгоритмы использует класс Car. Наблюдатель (Observer) Паттерн "Наблюдатель" (Observer) представляет поведенческий шаблон проектирования, который использует отношение "один ко многим". В этом отношении есть один наблюдаемый объект и множество наблюдателей. И при изменении наблюдаемого объекта автоматически происходит оповещение всех наблюдателей. Данный паттерн еще называют Publisher-Subscriber (издатель-подписчик), поскольку отношения издателя и подписчиков характеризуют действие данного паттерна: подписчики подписываются email-рассылку определенного сайта. Сайт-издатель с помощью email-рассылки уведомляет всех подписчиков о изменениях. А подписчики получают изменения и производят определенные действия: могут зайти на сайт, могут проигнорировать уведомления и т.д. Когда использовать паттерн Наблюдатель? Когда система состоит из множества классов, объекты которых должны находиться в согласованных состояниях Когда общая схема взаимодействия объектов предполагает две стороны: одна рассылает сообщения и является главным, другая получает сообщения и реагирует на них. Отделение логики обеих сторон позволяет их рассматривать независимо и использовать отдельно друга от друга. Когда существует один объект, рассылающий сообщения, и множество подписчиков, которые получают сообщения. При этом точное число подписчиков заранее неизвестно и процессе работы программы может изменяться. С помощью диаграмм UML данный шаблон можно выразить следующим образом: Формальное определение паттерна на языке C# может выглядеть следующим образом: 1 2 3 4 5 6 7 8 9 10 11 interface IObservable { void AddObserver(IObserver o); void RemoveObserver(IObserver o); void NotifyObservers(); } class ConcreteObservable : IObservable { private List { 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 observers = new List } public void AddObserver(IObserver o) { observers.Add(o); } public void RemoveObserver(IObserver o) { observers.Remove(o); } public void NotifyObservers() { foreach (IObserver observer in observers) observer.Update(); } } interface IObserver { void Update(); } class ConcreteObserver :IObserver { public void Update() { } } Участники IObservable: представляет наблюдаемый объект. Определяет три метода: AddObserver() (для добавления наблюдателя), RemoveObserver() (удаление набюдателя) и NotifyObservers() (уведомление наблюдателей) ConcreteObservable: конкретная реализация интерфейса IObservable. Определяет коллекцию объектов наблюдателей. IObserver: представляет наблюдателя, который подписывается на все уведомления наблюдаемого объекта. Определяет метод Update(), который вызывается наблюдаемым объектом для уведомления наблюдателя. ConcreteObserver: конкретная реализация интерфейса IObserver. При этом наблюдаемому объекту не надо ничего знать о наблюдателе кроме того, что тот реализует метод Update(). С помощью отношения агрегации реализуется слабосвязанность обоих компонентов. Изменения в наблюдаемом объекте не виляют на наблюдателя и наоборот. В определенный момент наблюдатель может прекратить наблюдение. И после этого оба объекта - наблюдатель и наблюдаемый могут продолжать существовать в системе независимо друг от друга. Рассмотрим реальный пример применения шаблона. Допустим, у нас есть биржа, где проходят торги, и есть брокеры и банки, которые следят за поступающей информацией и в зависимости от поступившей информации производят определенные действия: 1 2 3 class Program { static void Main(string[] args) 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 { Stock stock = new Stock(); Bank bank = new Bank("ЮнитБанк", stock); Broker broker = new Broker("Иван Иваныч", stock); // имитация торгов stock.Market(); // брокер прекращает наблюдать за торгами broker.StopTrade(); // имитация торгов stock.Market(); Console.Read(); } } interface IObserver { void Update(Object ob); } interface IObservable { void RegisterObserver(IObserver o); void RemoveObserver(IObserver o); void NotifyObservers(); } class Stock : IObservable { StockInfo sInfo; // информация о торгах List { observers = new List } public void RegisterObserver(IObserver o) { observers.Add(o); } public void RemoveObserver(IObserver o) { observers.Remove(o); } public void NotifyObservers() { foreach(IObserver o in observers) { o.Update(sInfo); 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 } } public void Market() { Random rnd = new Random(); sInfo.USD = rnd.Next(20, 40); sInfo.Euro = rnd.Next(30, 50); NotifyObservers(); } } class StockInfo { public int USD { get; set; } public int Euro { get; set; } } class Broker : IObserver { public string Name { get; set; } IObservable stock; public Broker(string name, IObservable obs) { this.Name = name; stock = obs; stock.RegisterObserver(this); } public void Update(object ob) { StockInfo sInfo = (StockInfo)ob; if(sInfo.USD>30) Console.WriteLine("Брокер {0} продает доллары; Курс доллара: {1}", this.Name, sInfo.USD); else Console.WriteLine("Брокер {0} покупает доллары; Курс доллара: {1}", this.Name, sInfo.USD); } public void StopTrade() { stock.RemoveObserver(this); stock=null; } } class Bank : IObserver { public string Name { get; set; } IObservable stock; public Bank(string name, IObservable obs) { this.Name = name; stock = obs; 108 109 110 111 112 113 114 115 116 117 118 119 stock.RegisterObserver(this); } public void Update(object ob) { StockInfo sInfo = (StockInfo)ob; if (sInfo.Euro > 40) Console.WriteLine("Банк {0} продает евро; Курс евро: {1}", this.Name, sInfo.Euro); else Console.WriteLine("Банк {0} покупает евро; Курс евро: {1}", this.Name, sInfo.Euro); } } Итак, здесь наблюдаемый объект представлен интерфейсом IObservable, а наблюдатель - интерфейсом IObserver. Реализацией интерфейса IObservable является класс Stock, который символизирует валютную биржу. В этом классе определен метод Market(), который имитирует торги и инкапсулирует всю информацию о валютных курсах в объекте StockInfo. После проведения торгов производится уведомление всех наблюдателей. Реализациями интерфейса IObserver являются классы Broker, представляющий брокера, и Bank, представляющий банк. При этом метод Update() интерфейса IObserver принимает в качестве параметра некоторый объект. Реализация этого метода подразумевает получение через данный параметр объекта StockInfo с текущей информацией о торгах и произведение некоторых действий: покупка или продажа долларов и евро. Дело в том, что часто необходимо информировать наблюдателя об изменении состояния наблюдаемого объекта. В данном случае состояние заключено в объекте StockInfo. И одним из вариантом информирования наблюдателя о состоянии является push-модель, при которой наблюдаемый объект передает (иначе говоря толкает - push) данные о своем состоянии, то есть передаем в виде параметра метода Update(). Альтернативой push-модели является pull-модель, когда наблюдатель вытягивает (pull) из наблюдаемого объекта данные о состоянии с помощью дополнительных методов. Также в классе брокера определен дополнительный метод StopTrade(), с помощью которого брокер может отписаться от уведомлений биржи и перестать быть наблюдателем. Практическая работа №19 Разработка приложения с использованием текстовых компонентов оздание простого консольного приложения C# в Visual Studio 10.02.2021 Чтение занимает 9 мин o o В этом учебнике по C# вы создадите и запустите консольное приложение с помощью Visual Studio, а также ознакомитесь с некоторыми возможностями интегрированной среды разработки (IDE) Visual Studio. Установите Visual Studio бесплатно со страницы скачиваемых материалов Visual Studio, если еще не сделали этого. Создание проекта Для начала мы создадим проект приложения C#. Для этого типа проекта уже имеются все нужные файлы шаблонов, что избавляет вас от лишней работы. 1. Запустите Visual Studio 2019. 2. На начальном экране выберите Создать проект. 3. В окне |