'

Технология разработки программного обеспечения (вторая часть) Поведенческие шаблоны проектирования ПО

Понравилась презентация – покажи это...





Слайд 0

Технология разработки программного обеспечения (вторая часть) Поведенческие шаблоны проектирования ПО проф. каф. ОСУ Тузовский А.Ф. Лекция 6


Слайд 1

3. Паттерны поведения Паттерны поведения (поведенческие паттерны) служат для управления различными вариантами поведения системы объектов (классов). Chain Of Responsibility Command State Template Method Mediator Interpreter Iterator Memento Observer Strategy Visitor


Слайд 2

Паттерн Command (Команда) Паттерн Command инкапсулирует (скрывает) команды в некотором объекте. Такое инкапсулирование позволяет выполнять различные действия, Например: управление выбором и последовательностью исполнения команд, возможность постановки команд в очередь, отмена команд и т.д.


Слайд 3

Паттерн Command является одним из самых простых и элегантных. Он включает интерфейс с единственной функцию, которая не имеет никаких параметров. Область применения этого паттерна практически безгранична. public interface Command { void Execute(); }


Слайд 4

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


Слайд 5

Диаграмма паттерна Command


Слайд 6

Участвующие элементы Command – Определяет интерфейс для исполнения операции ConcreteCommand Определяет связывание между объектом-получателем (Receiver) и действием Реализует исполнение путем вызова соответствующих операций Receiver Client – создает объект ConcreteCommand и устанавливает его получателя Invoker – запрашивает команду выполнить некоторый запрос Receiver – знает, как выполнить операции связанные с обработкой запроса


Слайд 7

Пример кода паттерна Command abstract class Command { // абстрактный класс 'Command' protected Receiver receiver; public Command (Receiver receiver) { this .receiver = receiver; } public abstract void Execute (); } class ConcreteCommand : Command { // ' ConcreteCommand' класс public ConcreteCommand (Receiver receiver) : base (receiver) {} public override void Execute () {receiver.Action();} } class Receiver { // 'Receiver' класс public void Action() { Console.WriteLine("Вызван метод Receiver.Action ()”);} } class Invoker { // 'Invoker' класс private Command _command; public void SetCommand(Command command) { this._command = command; } public void ExecuteCommand() { _command.Execute (); } }


Слайд 8

Пример кода использования паттерна Command using System; namespace Command { class MainApp { static void Main() { // Создаем объекты receiver, command, and invoker Receiver receiver = new Receiver(); Command command = new ConcreteCommand(receiver); Invoker invoker = new Invoker (); // Устанавливаем и запускаем команду invoker.SetCommand(command); invoker.ExecuteCommand(); Console.ReadLine(); } } }


Слайд 9

Использование паттерна Command для управления копировальным устройством Иерархия команд:


Слайд 10

Метод Execute() в классе RelayOnCommand – включает реле, в классе MotorOffCommand – выключает электродвигатель. Адреса электродвигателя или реле передаются объекту в качестве аргумента конструктора. При такой структуре объекты Command можно передавать между разными компонентами системы, которые будут вызывать метод Execute(), ничего не зная о том, какую именно команду они представляют. Это приводит к интересным упрощениям.


Слайд 11

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


Слайд 12

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


Слайд 13

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


Слайд 14

Программа инициализации будет читать этот файл и сконфигурировать систему. В результате, монтажная схема системы оказывается целиком вне самой программы ее можно изменять без повторной компиляции. За счет инкапсуляции понятия команды, паттерн Command позволяет отделить логические связи внутри системы от связываемых устройств.


Слайд 15

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


Слайд 16

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


Слайд 17

Диаграмма классов по работе с данными о сотруднике (расчет зар. платы)


Слайд 18

Объект AddEmployeeTransaction содержит те же поля, что и объект Employee, и указатель на объект PayClassification. создаются на основе данных, которые ввел пользователь, который описывает нового работника.


Слайд 19

Класс AddEmployeeTransaction


Слайд 20

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


Слайд 21

Объект Employee будет создаваться и инициализироваться значениями, взятыми из объекта AddEmployeeTransaction. В объект Employee будет также скопирован объект PayClassification (целиком или по ссылке).


Слайд 22

Физическое отделение частей программы Шаблон Command позволяет разорвать связи между частью программы, которая получает данные от пользователя, частью, которая проверяет и обрабатывает их, и самими бизнес-объектами. Если для добавления новых данных используется графический интерфейс (GUI), то совершенно неправильно включать в GUI код для проверки данных и их последующей обработки. Наличие такой связанности не позволит использовать код проверки и обработки в других интерфейсах. При включении этого кода в класс AddEmployeeTransaction он физически отделяется от интерфейса получения данных; отделяется код, знающий о том, как работать с БД, от самих бизнес-объектов (что еще важнее).


Слайд 23

Временное разделение частей программы Получив тем или иным способом данные, совсем не обязательно сразу же вызывать для них методы проверки и обработки. Объекты транзакций можно временно сохранить в списке, а проверить и обработать гораздо позже. Предположим, что в течение дня база данных не должна изменяться. Все изменения следует вносить только между полуночью и часом ночи. Глупо ждать до полуночи, а потом вводить все команды, стараясь успеть все сделать до часу. Удобно ввести команды в рабочее время, сразу же их проверить, а выполнение отложить до полуночи. Паттерн Command дает нам такую возможность.


Слайд 24

Часто паттерн Command включает метод Undo(). В классе, производном от Command, метод Execute() позволяет запомнить детали выполняемой операции, метод Undo() дает возможность откатить эту операцию и привести систему в исходное состояние. Использование паттерна Command для выполнения операции UnDo


Слайд 25

Рассмотрим программу, которая позволяет пользователю рисовать на экране геометрические фигуры. На панели инструментов есть кнопки Нарисовать круг, Нарисовать квадрат, Нарисовать прямоугольник, и т. д. На панели инструментов есть кнопка “Отменить”. Пример использования метода Undo()


Слайд 26

Система создает объект DrawCircleCommand и вызывает его метод Execute(). Объект DrawCircleCommand следит за состояние мыши, ожидая, когда пользователь нажмет левую кнопку: при нажатии он делает точку, где находится указатель мыши, центром круга начинает рисовать анимированный круг, следя за положением указателя, при отпускании кнопки рисует круг и сохраняет идентификатор нового круга в своей закрытой переменной. В конце метод Execute() возвращает управление и система помещается отработавший объект DrawCirlceCommand в стек выполненных команд. Пользователь нажал кнопку “Нарисовать круг”.


Слайд 27

Система извлекает из стека объекта Command и вызывает его метод Undo(). Получив сообщение Undo(), объект DrawCircleCommand удаляет круг с тем идентификатором, который в нем хранится, из списка объектов, нарисованных на холсте. Это позволяет без труда реализовать команду отмены практически в любом приложении. Код, знающий, как отменить команду, почти всегда находится рядом с кодом, знающим, как ее выполнить. Позже пользователь нажал кнопку “Отменить”


Слайд 28

Использование паттерна Command для имитации многопоточности Одно из интересных применений паттерна Command – это его использование в паттерне Active Object (Активный объект). Давно используется в тысячах промышленных систем для организации простого многопоточного ядра. Идея очень проста и показана на листинги последующих слайдов.


Слайд 29

Класс ActiveObjectEngine using System.Collections; public class ActiveObjectEngine { ArrayList itsCommands = new ArrayList(); public void AddCommand(Command c) { itsCommands.Add(c); } public void Run() { while (itsCommands.Count > 0) { Command c = (Command) itsCommands[0]; itsCommands.RemoveAt(0); c.Execute(); } }} ИнтерфейсCommand.cs public interface Command { void Execute(); }


Слайд 30

Объект ActiveObjectEngine хранит связанный список объектов Command. Пользователь может добавлять в него новые команды вызывать метод Run(). Метод Run() проходит по списку команд; выполняет каждую встретившуюся команду; затем удаляет ее.


Слайд 31

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


Слайд 32

Рассмотрим тест Он создает объект SleepCommand, передавая его конструктору среди прочего и величину задержки 1000 мс. Затем объект SleepCommand помещается в ActiveObjectEngine. Тест ожидает, что после вызова Run() должно пройти не менее 1000 мс.


Слайд 33

Взглянем на этот тест внимательнее. У конструктора SleepCommand есть три аргумента. Первый – время задержки в миллисекундах. Второй – объект ActiveObjectEngine, внутри которого будет работать команда. А третий, wakeup, – еще одна команда, которую следует вызвать при возобновлении работы, то есть по прошествии указанного числа миллисекунд.


Слайд 34

Класс SleepCommand using System; public class SleepCommand : Command { private Command wakeupCommand = null; private ActiveObjectEngine engine = null; private long sleepTime = 0; private DateTime startTime; private bool started = false; public SleepCommand( long milliseconds, ActiveObjectEngine e, Command wakeupCommand) { sleepTime = milliseconds; engine = e; this.wakeupCommand = wakeupCommand; } public void Execute() { DateTime currentTime = DateTime.Now; if (!started) { started = true; startTime = currentTime; engine.AddCommand(this); } else { TimeSpan elapsedTime = currentTime - startTime; if (elapsedTime.TotalMilliseconds < sleepTime) { engine.AddCommand(this); } else { engine.AddCommand(wakeupCommand); } } } }


Слайд 35

При выполнении объект проверяет, исполнялся ли он раньше, и если нет, то запоминает время начала работы. Если задержка еще не истекла, то объект помещает себя обратно в ActiveObjectEngine. В противном случае в ActiveObjectEngine помещается команда wakeup. Данная программа аналогична многопоточной программе, ожидающей события. Когда поток в многопоточной программе ждет события, он обычно делает вызов ОС, который блокирует поток, пока событие не произойдет.


Слайд 36

Показанная выше программа не блокируется. Но если ожидаемое событие не произошло в течение времени elapsedTime.TotalMilliseconds, то поток просто помещает себя назад в объект ActiveObjectEngine. Построение многопоточных систем с использованием этой техники было и остается весьма распространенной практикой. Такие потоки называются исполняемыми до завершения (Run-to-Completion – RTC); каждый экземпляр Command отрабатывает до конца, прежде чем запускается следующий экземпляр. Аббревиатура RTC подразумевает, что экземпляры Command не блокируют программу.


Слайд 37

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


Слайд 38

Программа DelayedTyper using System; public class DelayedTyper : Command { private long itsDelay; private char itsChar; private static bool stop = false; private static ActiveObjectEngine engine = new ActiveObjectEngine(); private class StopCommand : Command { public void Execute() { DelayedTyper.stop = true; } } public static void Main(string[] args) { engine.AddCommand(new DelayedTyper(100, ‘1’)); engine.AddCommand(new DelayedTyper(300, ‘3’)); engine.AddCommand(new DelayedTyper(500, ‘5’)); engine.AddCommand(new DelayedTyper(700, ‘7’)); Command stopCommand = new StopCommand(); engine.AddCommand( new SleepCommand(20000, engine, stopCommand)); engine.Run(); } public DelayedTyper(long delay, char c) { itsDelay = delay; itsChar = c; } public void Execute() { Console.Write(itsChar); if (!stop) DelayAndRepeat(); } private void DelayAndRepeat() { engine.AddCommand( new SleepCommand(itsDelay, engine, this)); } }


Слайд 39

Класс DelayedTyper реализует интерфейс Command. Метод Execute() печатает символ, переданный конструктору, проверяет флаг stop если флаг stop не равен true, вызывает метод DelayAndRepeat(). Метод DelayAndRepeat() создает объект SleepCommand с задержкой, величина которой передана конструктору, включает этот объект в список объекта ActiveObjectEngine.


Слайд 40

Два типичных прогона: 135711311511371113151131715131113151731111351113711531111357... 135711131513171131511311713511131151731113151131711351113117... Причина различия – таймер процессора и таймер реального времени синхронизированы не идеально. Такое поведение отличительный признаком многопоточных систем. источник неприятностей, разочарований и огорчений. создает проблему с отладкой.


Слайд 41

Поведение этого объекта Command легко предсказать. Он работает в цикле, печатая заданный символ и ожидая истечения задержки. Выход из цикла происходит, когда флаг stop = true. Метод Main () создает несколько объектов DelayedTyper, каждый со своим символом и задержкой, помещает их в ActiveObjectEngine, добавляет туда же команду SleepCommand, которая по истечении некоторого времени установит флаг stop. Если запустить эту программу, то будет напечатана строка, содержащая символы 1, 3, 5 и 7. При повторном запуске будет напечатана другая строка, состоящая из тех же символов.


Слайд 42

Выводы по паттерну Command Простота паттерна Command создает ложное впечатление о его возможностях. Данный паттерн можно использовать для самых разных целей: управления устройствами, для реализации транзакций базы данных, выполнения и отмены операций в графическом интерфейсе пользователя. имитации многопоточного ядра, Высказывалось мнение, что паттерн Command не согласуется с ООП – на передний план выдвигаются не классы, а функции. Возможно, это и так, но разработчик реального ПО отдает предпочтение полезности, а не теории. Паттерн Command может быть весьма полезен.


Слайд 43

Паттерн State Паттерн State заключает состояния объекта в отдельные объекты, каждый из которых расширяет общий суперкласс.


Слайд 44

Участники паттерна State Context Определяет интерфейс для клиентов Поддерживает объект наследника ConcreteState, определяющего текущее состояние State – Определяет интерфейс для инкапсуляции поведения, связанного с состоянием Context. ConcreteState – наследник интерфейса Context реализует поведение, связанное с конкретным состоянием Context.


Слайд 45

Пример использования паттерна State using System; namespace State { class MainApp { static void Main() { // Создаём контекст в определенном состоянии Context с = new Context(new ConcreteStateA()); // Вызываем операции, которые переключают состояние с.Request () ; с.Request () ; с.Request () ; с.Request () ; Console.ReadLine() ; } // State абстрактный класс abstract class State { public abstract void Handle(Context context); }


Слайд 46

// ConcreteStateA класс class ConcreteStateA : State { public override void Handle (Context context) { context.State = new ConcreteStateB ();} } // ConcreteStateB class class ConcreteStateB : State { public override void Handle (Context context) { context . State = new ConcreteStateA () ; } } // Context класс class Context { private State state; public Context(State state) { this.State = state;} public State State { get { return _state; } set { _state = value; Console.WriteLine("Состояние: “ + state.GetType().Name); } } public void Request() { state.Handle(this) ;} } }


Слайд 47

Паттерн Mediator (Посредник) Паттерн Mediator используется для согласования изменений состояний набора объектов с помощью одного объекта. Вместо раскидывания логики поведения по разным классам данный паттерн инкапсулирует логику управления изменением состояний в рамки одного класса.


Слайд 48

UML диаграмму паттерна Mediator (Посредник)


Слайд 49

Участники паттерна Mediator (Посредник) Класс Mediator Определяет интерфейс для общения с объектами Colleague ConcreteMediator Реализует совместное поведение путем координирования объектов Colleague Знает и поддерживает своих Colleague Colleague классы Каждый Colleague класс знает своего Mediator Каждый Colleague общается со своим медиатором


Слайд 50

Пример кода (показывает принцип работы Mediator) using System; namespace Mediator { class MainApp { static void Main() { ConcreteMediator m = new ConcreteMediator (); ConcreteColleague1 c1 = new ConcreteColleague1 (m) ; ConcreteColleague2 c2 = new ConcreteColleague2 (m) ; m.Colleague1 = c1; m.Colleague2 = c2 ; c1.Send("Как дела?"); c2.Send("Хорошо спасибо"); Console.ReadLine(); } } // 'Mediator' абстрактный класс abstract class Mediator { public abstract void Send(string message, Colleague colleague); } // 'ConcreteMediator' класс class ConcreteMediator : Mediator { private ConcreteColleague1 _colleague1; private ConcreteColleague2 _colleague2; public ConcreteColleaguel Colleague1 { set { _colleague1 = value; } } public ConcreteColleague2 Colleague2 { set { _colleague2 = value; } } public override void Send(string message, Colleague colleague) { if (colleague == _colleague1) _colleague2.Notify(message) else _colleaguel.Notify(message); } } } // 'Colleague' абстрактный класс abstract class Colleague { protected Mediator mediator; public Colleague(Mediator mediator) // Конструктор { this.mediator = mediator; } } // 'ConcreteColleague1' класс class ConcreteColleague1 : Colleague { // Конструктор public ConcreteColleaguel(Mediator mediator) : base(mediator) { } public void Send(string message) { mediator.Send(message, this); } public void Notify(string message) { Console.WriteLine("Colleague1 получил сообщение: "+ message); } } // 'ConcreteColleague2' класс class ConcreteColleague2 : Colleague { // Конструктор public ConcreteColleague2 (Mediator mediator) : base (mediator } {} public void Send(string message) { mediator.Send(message, this); } public void Notify(string message) { Console.WriteLine("Colleague2 получил сообщение: " + message); } } }


Слайд 51

Паттерны Template Methоd (Шаблонный метод) и Strategy (Стратегия) В начале 1990-х годов, когда ОО технологии только зарождались, всех захватила идея наследования. Это отношение обещало грандиозные перспективы. С помощью наследования можно было программировать только различия! имея класс, делающий нечто полезное, можно создать его подкласс и изменить лишь те части, которые нас не устраивали. Появилась возможность повторно использовать код, просто унаследовав его! Можно организовывать целые иерархии программных конструкций, в которых на каждом уровне использовался код с предыдущих уровней. Открылся прекрасный новый мир.


Слайд 52

Но, как и большинство прекрасных новых миров, этот на поверку тоже оказался не вполне пригодным к обитанию. К 1995 году стало понятно, что наследованием очень просто злоупотребить, а обходится такое злоупотребление крайне дорого. Гамма, Хелм, Джонсон и Влиссидес советуют: «Отдавайте предпочтение композиции объектов, а не наследованию классов». стали реже применять наследование, чаще заменять его композицией или делегированием.


Слайд 53

Иллюстрация различий между наследованием и делегированием Для решения сходных задач (и нередко взаимозаменяемо) используют шаблоны: Шаблон Template Method (Шаблонный метод) и Шаблон Стратегия (Strategy) Иллюстрируют различие между наследованием и делегированием. в Шаблонном методе применяется наследование, в Стратегии – делегирование.


Слайд 54

Решаемая задача Шаблоны Template Methоd и Strategy решают задачу отделения общего алгоритма от конкретного контекста. это очень часто требуется при проектировании ПО. Задача: имеется алгоритм общего вида, применимый к разным ситуациям. Нужно сделать, чтобы этот алгоритм не зависел от деталей реализации. алгоритм и конкретная реализация должны зависеть только от абстракций. в соответствии с принципом инверсии зависимости (Dependency-Inversion Principle)


Слайд 55

Пример использования паттерна Template Method Например, многие программы имеют следующую структуру: Initialize(); // инициализация приложения while (!Done()) {// основной цикл doWork(); // делаем что-то полезное. } Cleanup(); // делаем зачистку


Слайд 56

Сначала выполняется инициализация приложения. Затем входим в главный цикл, где программа делает то, для чего написана. например, обработка событий GUI или записей базы данных. Когда все сделано, выполняется выход из главного цикла Делается зачистка.


Слайд 57

Пример программы using System; using System.IO; public class FtoCRaw { public static void Main(string[] args) { bool done = false; while (!done) { string fahrString = Console.In.ReadLine(); if (fahrString == null || fahrString.Length == 0) done = true; else { double fahr = Double.Parse(fahrString); double celcius = 5.0/9.0*(fahr - 32); Console.Out.WriteLine(“F={0}, C={1}”,fahr,celcius); } } Console.Out.WriteLine(“ftoc exit”); } }


Слайд 58

В данной программе имеются все элементы рассмотренного главного цикла Небольшая инициализация, Содержательная работа в цикле, Затем очистка и выход. Отделить базовую структуру от конкретной программы ftoc позволяет паттерн Template Methоd.


Слайд 59

Паттерн Template Methоd Алгоритм, описывающий общую структуру, описывается в имеющем реализацию методе абстрактного базового класса. Детали выносятся в абстрактные методы. Например, общая структура программы очень распространена, ее можно инкапсулировать в класс Application. делать каждую новую программе производной от класса Application.


Слайд 60

Класс Application public abstract class Application { private bool isDone = false; protected abstract void Init(); protected abstract void Idle(); protected abstract void Cleanup(); protected void SetDone() { isDone = true; } protected bool Done() { return isDone; } public void Run() { Init(); while (!Done()) Idle(); Cleanup(); } }


Слайд 61

Общая структура приложения с главным циклом Цикл находится в реализованном методе Run(). Работа программы вынесена в абстрактные методы Init(), Idle() и Cleanup(). Метод Init() берет на себя инициализацию. Метод Idle() выполняет основную работу программы вызывается до тех пор, пока Done() возвращает false. Метод Cleanup() отвечает за очистку перед выходом.


Слайд 62

Программа с использованием шаблона Template Methоd using System; using System.IO; public class FtoCTemplateMethod : Application { private TextReader input; private TextWriter output; public static void Main (string[] args) { new FtoCTemplateMethod().Run(); } protected override void Init() { input = Console.In; output = Console.Out; } protected override void doWork() { string fahrString = input.ReadLine(); if (fahrString == null || fahrString.Length == 0) SetDone(); else { double fahr = Double.Parse(fahrString); double celcius = 5.0/9.0*(fahr - 32); output.WriteLine(“F={0}, C={1}”, fahr, celcius); } } protected override void Cleanup() { output.WriteLine(“ftoc exit”); } }


Слайд 63

Анализ использования шаблона Применять Template Methоd для данного конкретного приложения не обосновано. лишь усложняет и увеличивает программу. Инкапсуляция главного цикла всех возможных приложений поначалу кажется хорошей идеей, но ее практическое воплощение в данном случае не приносит никаких реальных результатов. Паттерны проектирования – хорошие способы. могут помочь в решении многих задач проектирования. Однако не нужно злоупотреблять их использованием. Хотя к данному случаю можно применить Template Methоd, но использовать его не стоит. Издержки превышают выгоду.


Слайд 64

Класс для пузырьковой сортировки public class BubbleSorter { static int operations = 0; public static int Sort(int [] array) { operations = 0; if (array.Length <= 1) return operations; for (int nextToLast = array.Length-2; nextToLast >= 0; nextToLast--) for (int index = 0; index <= nextToLast; index++) CompareAndSwap(array, index); return operations; } private static void Swap(int[] array, int index) { int temp = array[index]; array[index] = array[index+1]; array[index+1] = temp; } private static void CompareAndSwap(int[] array, int index) { if (array[index] > array[index+1]) Swap(array, index); operations++; } }


Слайд 65

Класс BubbleSorter знает, как сортировать массив целых чисел, применяя алгоритм пузырьковой сортировки. Метод Sort() содержит сам алгоритм пузырьковой сортировки Два вспомогательных метода Swap() и CompareAndSwap() – посвящены деталям, связанным с целыми числами и массивами.


Слайд 66

С помощью паттерна Template Methоd можно выделить алгоритм пузырьковой сортировки в абстрактный базовый класс BubbleSorter. Он содержит реализацию метода Sort(), который вызывает абстрактные методы OutOfOrder() и Swap(). Метод OutOfOrder() сравнивает два соседних элемента массива и возвращает true, если они расположены не по порядку. Метод Swap() переставляет местами два соседних элемента массива.


Слайд 67

Метод Sort() ничего не знает о массиве, ему все равно, какие в нем хранятся объекты. Он вызывает OutOfOrder(), передавая ему индекс элемента в массиве, и узнает, нужно ли переставить соседние элементы.


Слайд 68

Абстрактный класс сортировки public abstract class BubbleSorter { private int operations = 0; protected int length = 0; protected int DoSort() { operations = 0; if (length <= 1) return operations; for (int nextToLast = length-2; nextToLast >= 0; nextToLast--) for (int index = 0; index <= nextToLast; index++) { if (OutOfOrder(index)) Swap(index); operations++; } return operations; } protected abstract void Swap(int index); protected abstract bool OutOfOrder(int index); }


Слайд 69

класс IntBubbleSorter будет сортировать массивы целых чисел; класс DoubleBubbleSorter – массивы чисел с двойной точностью.


Слайд 70

Реализация класса IntBubbleSorter public class IntBubbleSorter : BubbleSorter { private int[] array = null; public int Sort(int[] theArray) { array = theArray; length = array.Length; return DoSort(); } protected override void Swap(int index) { int temp = array[index]; array[index] = array[index + 1]; array[index + 1] = temp; } protected override bool OutOfOrder(int index) { return (array[index] > array[index + 1]); } }


Слайд 71

Реализация класса DoubleBubbleSorter public class DoubleBubbleSorter : BubbleSorter { private double[] array = null; public int Sort(double[] theArray) { array = theArray; length = array.Length; return DoSort(); } protected override void Swap(int index) { double temp = array[index]; array[index] = array[index + 1]; array[index + 1] = temp; } protected override bool OutOfOrder(int index) { return (array[index] > array[index + 1]); } }


Слайд 72

Выводы Паттерн Template Methоd – пример классической формы повторного использования в ОО программировании. Обобщенный алгоритм помещается в базовый класс, который наследуется в различных конкретных контекстах. Такой способ имеет свои недостатки: наследование – очень сильное отношение.


Слайд 73

Подклассы неразрывно связаны со своими базовыми классами. Например, методы OutOfOrder и Swap, реализованные в классе IntBubbleSorter, – это то, что необходимо алгоритму сортировки. Использовать их в других алгоритмах уже нельзя. Унаследовав класс IntBubbleSorter от BubbleSorter, они навечно привязаны один к другому. Паттерн Стратегия предлагает иной подход.


Слайд 74

Паттерн Strategy (Стратегия) Паттерн Strategy решает проблему инверсии зависимости совсем по-другому. Рассмотрим класс Application использующий паттерн Template Methоd. Общий алгоритм работы приложения помещается не в абстрактный базовый класс, а в конкретный класс ApplicationRunner.


Слайд 75

Паттерн Strategy (Стратегия) Абстрактные методы, которые может вызывать общий алгоритм, определим в интерфейсе Application. создадим производный от (интерфейса) Application класс FtoCStrategy и передадим его в ApplicationRunner. Таким образом, ApplicationRunner делегирует содержательную работу этому интерфейсу.


Слайд 76

Интерфейс Application public interface Application { void Init(); void doWork(); void Cleanup(); bool Done(); }


Слайд 77

Класс ApplicationRunner public class ApplicationRunner { private Application itsApplication = null; public ApplicationRunner(Application app) {itsApplication = app;} public void run(){ itsApplication.Init(); while (!itsApplication.Done()) itsApplication.doWork(); itsApplication.Cleanup(); } }


Слайд 78

Класс FtoCStrategy using System; using System.IO; public class FtoCStrategy : Application { private TextReader input; private TextWriter output; private bool isDone = false; public static void Main(string[] args) { FtoCStrategy fcs = new FtoCStrategy(); ApplicationRunner apr = new ApplicationRunner(fcs); apr.run(); } public void Init() { input = Console.In; output = Console.Out; } public void doWork() { string fahrString = input.ReadLine(); if (fahrString == null || fahrString.Length == 0) isDone = true; else { double fahr = Double.Parse(fahrString); double celcius = 5.0/9.0*(fahr - 32); output.WriteLine(“F={0}, C={1}”, fahr, celcius); }}


Слайд 79

Недостатки Strategy по сравнению с Template Methоd В паттерне Strategy больше классов и выше уровень косвенности, чем в Template Methоd. Делегирование по указателю в ApplicationRunner обходится с точки зрения времени и памяти чуть дороже, чем наследование.


Слайд 80

Достоинства Strategy по сравнению с Template Methоd Если нужно запускать много приложений, то можно использовать один экземпляр ApplicationRunner и передавать ему различные реализации Application, что позволит сэкономить память.


Слайд 81

Однако ни недостатки, ни достоинства сами по себе не являются определяющими. В большинстве случаев они пренебрежимо малы. Обычно наибольшее беспокойство вызывает дополнительный класс, необходимый в паттерне Strategy. Однако это еще не все. Рассмотрим реализацию пузырьковой сортировки на основе паттерна Strategy.


Слайд 82

Интерфейс SortHandler public interface SortHandler { void Swap(int index); bool OutOfOrder(int index); int Length(); void SetArray(object array); }


Слайд 83

Класс BubbleSorter public class BubbleSorter { private int operations = 0; private int length = 0; private SortHandler itsSortHandler = null; public BubbleSorter(SortHandler handler) { itsSortHandler = handler; } public int Sort(object array) { itsSortHandler.SetArray(array); length = itsSortHandler.Length(); operations = 0; if (length <= 1) return operations; for (int nextToLast = length – 2; nextToLast >= 0; nextToLast--) for (int index = 0; index <= nextToLast; index++) { if (itsSortHandler.OutOfOrder(index)) itsSortHandler.Swap(index); operations++; } return operations; } }


Слайд 84

Класс IntSortHandler public class IntSortHandler : SortHandler { private int[] array = null; public void Swap(int index) { int temp = array[index]; array[index] = array[index + 1]; array[index + 1] = temp; } public void SetArray(object array) { this.array = (int[]) array; } public int Length() { return array.Length; } public bool OutOfOrder(int index) { return (array[index] > array[index + 1]);} }


Слайд 85

В шаблоне Strategy класс IntSortHandler ничего не знает о BubbleSorter и никак не зависит от реализации пузырьковой сортировки. В шаблоне Template Methоd дело обстоит иначе. IntBubbleSorter напрямую зависит от класса BubbleSorter, содержащего алгоритм пузырьковой сортировки. Это отчасти нарушает принцип инверсии зависимости. Реализация методов Swap и OutOfOrder зависит от алгоритма пузырьковой сортировки.


Слайд 86

В паттерне Strategy такой зависимости нет класс IntSortHandler можно использовать и с другими реализациями сортировщика, а не только с BubbleSorter. Например, можно создать вариант пузырьковой сортировки, который прекращал бы работу, как только на очередном проходе по массиву выясняется, что он уже отсортирован. Такой класс QuickBubbleSorter мог бы воспользоваться классом IntSortHandler или любым другим, производным от SortHandler.


Слайд 87

Класс QuickBubbleSorter public class QuickBubbleSorter { private int operations = 0; private int length = 0; private SortHandler itsSortHandler = null; public QuickBubbleSorter(SortHandler handler) { itsSortHandler = handler; } public int Sort(object array) { itsSortHandler.SetArray(array); length = itsSortHandler.Length(); operations = 0; if (length <= 1) return operations; bool thisPassInOrder = false; for (int nextToLast = length-2; nextToLast >= 0 && !thisPassInOrder; nextToLast--) { thisPassInOrder = true; //potenially. for (int index = 0; index <= nextToLast; index++) { if (itsSortHandler.OutOfOrder(index)) { itsSortHandler.Swap(index); thisPassInOrder = false; } operations++; } } return operations; } }


Слайд 88

Преимущество Strategy по сравнению с Template Methоd Template Methоd позволяет подставлять в общий алгоритм различные детальные реализации, Strategy (в полном соответствии с принципом DIP) кроме этого разрешает использовать любую детальную реализацию в различных общих алгоритмах.


Слайд 89

Выводы Паттерн Template Methоd легко использовать на практике, но он недостаточно гибок. Паттерн Strategy обладает нужной гибкостью, но приходится вводить дополнительный класс, создавать дополнительный объект и инкорпорировать его в систему. Выбор между этими паттернами зависит от того, нужна ли гибкость Strategy или готовы удовольствоваться простотой Template Methоd.


Слайд 90

Паттерн Mediator (Посредник) Например, класс QuickEntryMediator находится за сценой и привязывает текстовое поле ввода к списку. Когда вы вводите текст в поле, первый элемент списка, начинающийся с введенной строки, подсвечивается. Это позволяет набирать только начало текста и затем производить быстрый выбор из списка.


Слайд 91

Класс QuickEntryMediator Принимает объекты TextBox и ListBox. Предполагается, что пользователь будет вводить в TextBox префиксы строк, находящихся в ListBox Класс автоматически выбирает первый элемент ListBox, который начинается с префикса, введенного в TextBox. Если значение в поле TextBox равно null или префикс не соответствует никакому элементу ListBox, то выделение в ListBox снимается. В этом классе нет открытых методов. Нужно просто создать объект класса и QuickEntryMediator и забываете о его существовании. Например: TextBox t = new TextBox(); ListBox l = new ListBox(); QuickEntryMediator qem = new QuickEntryMediator(t, l);


Слайд 92

Kласс QuickEntryMediator using System; using System.Windows.Forms; public class QuickEntryMediator { private TextBox itsTextBox; private ListBox itsList; public QuickEntryMediator(TextBox t, ListBox l) { itsTextBox = t; itsList = l; itsTextBox.TextChanged += new EventHandler(TextFieldChanged); } private void TextFieldChanged(object source, EventArgs args) { string prefix = itsTextBox.Text; if (prefix.Length == 0) { itsList.ClearSelected(); return; } ListBox.ObjectCollection listItems = itsList.Items; bool found = false; for (int i = 0; found == false && i < listItems.Count; i++) { object o = listItems[i]; string s = o.ToString(); if (s.StartsWith(prefix)) { itsList.SetSelected(i, true); found = true; } } if (!found) { itsList.ClearSelected(); } } }


Слайд 93

Структура класса QuickEntryMediator Конструктору экземпляра QuickEntryMediator передаются ссылки на ListBox и TextBox. QuickEntryMediator назначает обработчик события TextChanged для объекта TextBox. при любом изменении текста вызывает метод TextFieldChanged, который ищет в списке ListBox элемент, начинающийся с текущего значения текстового поля, и выделяет его. Пользователи классов ListBox и TextField понятия не имеют о существовании этого Посредника. Он находится в сторонке и незаметно накладывает свою политику на объекты, не спрашивая у них разрешения и даже не ставя их в известность.


Слайд 94

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


Слайд 95

Паттерн Наблюдатель (Observer)


Слайд 96


Слайд 97


Слайд 98


Слайд 99


Слайд 100


Слайд 101


Слайд 102


Слайд 103


Слайд 104


Слайд 105


Слайд 106


Слайд 107


Слайд 108


Слайд 109


Слайд 110


Слайд 111


Слайд 112


Слайд 113


Слайд 114


Слайд 115


Слайд 116


Слайд 117


Слайд 118


Слайд 119


Слайд 120


Слайд 121


Слайд 122


Слайд 123


Слайд 124


Слайд 125

Паттерн Наблюдатель


Слайд 126


Слайд 127


Слайд 128


Слайд 129


Слайд 130


Слайд 131

Абстрактный сервер, адаптер и мост


Слайд 132


Слайд 133


Слайд 134


Слайд 135


Слайд 136


×

HTML:





Ссылка: