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

Содержание

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

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

А.Ф.
Лекция 6



Слайд 23. Паттерны поведения
Паттерны поведения (поведенческие паттерны) служат для управления различными вариантами

поведения системы объектов (классов).

Chain Of Responsibility
Command
State
Template Method
Mediator
Interpreter
Iterator
Memento
Observer
Strategy
Visitor



Слайд 3Паттерн Command (Команда)
Паттерн Command инкапсулирует (скрывает) команды в некотором объекте.
Такое

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


Слайд 4
Паттерн Command является одним из самых простых и элегантных.
Он включает

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


Слайд 5
Паттерн Command находится на границе между объектом и функцией.
С точки зрения

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


Слайд 6Диаграмма паттерна Command


Слайд 7Участвующие элементы
Command – Определяет интерфейс для исполнения операции
ConcreteCommand
Определяет связывание между

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





Слайд 8Пример кода паттерна 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 (); }
}


Слайд 9Пример кода использования паттерна 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();
} } }


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


Слайд 11
Метод Execute()
в классе RelayOnCommand – включает реле,
в классе MotorOffCommand

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


Слайд 12Система управлялась событиями
В зависимости от происходящих в системе событий.
реле замыкаются и

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


Слайд 13
У такой простой структуры есть одно огромное преимущество.
Класс Sensor понятия

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







Слайд 14
Задание того, какие реле замыкать при поступлении событий от определенных датчиков,

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

Слайд 15
Программа инициализации будет читать этот файл и сконфигурировать систему.
В результате,


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


Слайд 16Использование паттерна Command для обработки транзакций
Паттерн Command широко применяется для создания

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





Слайд 17
Прежде чем работать с введенной информацией, программа должна проверить,
синтаксическую правильность
семантическую

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


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


Слайд 19
Объект AddEmployeeTransaction содержит
те же поля, что и объект Employee, и


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





Слайд 20Класс AddEmployeeTransaction


Слайд 21
Метод Validate() исследует все данные и убеждается, что они осмысленны.
проверяет

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





Слайд 22
Объект Employee будет создаваться и инициализироваться значениями, взятыми из объекта AddEmployeeTransaction.


В объект Employee будет также скопирован объект PayClassification (целиком или по ссылке).





Слайд 23Физическое отделение частей программы
Шаблон Command позволяет разорвать связи между
частью программы,

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




Слайд 24Временное разделение частей программы
Получив тем или иным способом данные, совсем не

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




Слайд 25Часто паттерн Command включает метод Undo().
В классе, производном от Command,
метод

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

Использование паттерна Command для выполнения операции UnDo


Слайд 26Рассмотрим программу, которая позволяет пользователю рисовать на экране геометрические фигуры.
На

панели инструментов есть кнопки
Нарисовать круг,
Нарисовать квадрат,
Нарисовать прямоугольник,
и т. д.
На панели инструментов есть кнопка “Отменить”.

Пример использования метода Undo()


Слайд 27Система создает объект DrawCircleCommand и вызывает его метод Execute().
Объект DrawCircleCommand

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




Пользователь нажал кнопку “Нарисовать круг”.


Слайд 28Система извлекает из стека объекта Command и вызывает его метод Undo().


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




Позже пользователь нажал кнопку “Отменить”


Слайд 29Использование паттерна Command для имитации многопоточности
Одно из интересных применений паттерна Command

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



Слайд 30Класс 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();
}


Слайд 31
Объект ActiveObjectEngine хранит связанный список объектов Command.
Пользователь может
добавлять в

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


Слайд 32
Что произойдет, если какой-то из находившихся в списке объектов Command поместит

себя обратно в список
получается бесконечный цикл.
Список никогда не опустеет и метод Run() будет работать вечно.


Слайд 33Рассмотрим тест
Он создает объект SleepCommand, передавая его конструктору среди прочего и

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



Слайд 34
Взглянем на этот тест внимательнее.
У конструктора SleepCommand есть три аргумента.


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


Слайд 35Класс 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);
} } } }


Слайд 36
При выполнении объект проверяет, исполнялся ли он раньше, и если нет,

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

Слайд 37
Показанная выше программа не блокируется.
Но если ожидаемое событие не произошло

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



Слайд 38
То, что экземпляры Command выполняются до конца дает RTC-потокам преимущество
они

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


Слайд 39Программа 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));
}
}


Слайд 40
Класс DelayedTyper реализует интерфейс Command.
Метод Execute()
печатает символ, переданный конструктору,


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


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

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


Слайд 42
Поведение этого объекта Command легко предсказать.
Он работает в цикле, печатая

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

Слайд 43Выводы по паттерну Command
Простота паттерна Command создает ложное впечатление о его

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


Слайд 44Паттерн State
Паттерн State заключает состояния объекта в отдельные объекты, каждый из

которых расширяет общий суперкласс.

Слайд 45Участники паттерна State
Context
Определяет интерфейс для клиентов
Поддерживает объект наследника ConcreteState, определяющего

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



Слайд 46Пример использования паттерна 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);
}

Слайд 47

// 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) ;}
} }



Слайд 48Паттерн Mediator (Посредник)
Паттерн Mediator используется для согласования изменений состояний набора объектов

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


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


Слайд 50Участники паттерна Mediator (Посредник)
Класс Mediator
Определяет интерфейс для общения с объектами Colleague


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


Слайд 51Пример кода (показывает принцип работы 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); }
}
}



Слайд 52Паттерны Template Methоd (Шаблонный метод) и Strategy (Стратегия)
В начале 1990-х годов,

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




Слайд 53
Но, как и большинство прекрасных новых миров, этот на поверку тоже

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



Слайд 54Иллюстрация различий между наследованием и делегированием
Для решения сходных задач (и нередко

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

Слайд 55Решаемая задача
Шаблоны Template Methоd и Strategy решают задачу отделения общего алгоритма

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




Слайд 56Пример использования паттерна Template Method
Например, многие программы имеют следующую структуру:
Initialize(); //

инициализация приложения
while (!Done()) {// основной цикл
doWork(); // делаем что-то полезное.
}
Cleanup(); // делаем зачистку




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

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



Слайд 58Пример программы
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”);
} }



Слайд 59В данной программе имеются все элементы рассмотренного главного цикла
Небольшая инициализация,
Содержательная

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

Слайд 60Паттерн Template Methоd
Алгоритм, описывающий общую структуру, описывается в имеющем реализацию методе

абстрактного базового класса.
Детали выносятся в абстрактные методы.

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


Слайд 61Класс 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();
}
}



Слайд 62Общая структура приложения с главным циклом
Цикл находится в реализованном методе Run().


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



Слайд 63Программа с использованием шаблона 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”); }
}



Слайд 64Анализ использования шаблона
Применять Template Methоd для данного конкретного приложения не обосновано.


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



Слайд 65Класс для пузырьковой сортировки
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++;
}
}


Слайд 66
Класс BubbleSorter знает, как сортировать массив целых чисел, применяя алгоритм пузырьковой

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

Слайд 67
С помощью паттерна Template Methоd можно выделить алгоритм пузырьковой сортировки в

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


Слайд 68
Метод Sort() ничего не знает о массиве, ему все равно, какие

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


Слайд 69Абстрактный класс сортировки
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);
}




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

с двойной точностью.

Слайд 71Реализация класса 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]);
}
}


Слайд 72Реализация класса 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]);
}
}


Слайд 73Выводы
Паттерн Template Methоd – пример классической формы повторного использования в ОО

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

Слайд 74
Подклассы неразрывно связаны со своими базовыми классами.
Например, методы OutOfOrder и Swap,

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

Слайд 75Паттерн Strategy (Стратегия)
Паттерн Strategy решает проблему инверсии зависимости совсем по-другому.
Рассмотрим

класс Application использующий паттерн Template Methоd.
Общий алгоритм работы приложения помещается
не в абстрактный базовый класс,
а в конкретный класс ApplicationRunner.


Слайд 76Паттерн Strategy (Стратегия)
Абстрактные методы, которые может вызывать общий алгоритм, определим в

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


Слайд 77Интерфейс Application
public interface Application {
void Init();
void

doWork();
void Cleanup();
bool Done();
}


Слайд 78Класс 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();
}
}


Слайд 79Класс 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);
}}

Слайд 80Недостатки Strategy по сравнению с Template Methоd
В паттерне Strategy больше классов

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

Слайд 81Достоинства Strategy по сравнению с Template Methоd
Если нужно запускать много приложений,

то
можно использовать один экземпляр ApplicationRunner и
передавать ему различные реализации Application,
что позволит сэкономить память.


Слайд 82
Однако ни недостатки, ни достоинства сами по себе не являются определяющими.


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


Слайд 83Интерфейс SortHandler
public interface SortHandler {
void Swap(int index);

bool OutOfOrder(int index);
int Length();
void SetArray(object array);
}

Слайд 84Класс 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;
}
}


Слайд 85Класс 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]);}
}


Слайд 86
В шаблоне Strategy класс IntSortHandler ничего не знает о BubbleSorter и

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



Слайд 87
В паттерне Strategy такой зависимости нет
класс IntSortHandler можно использовать и с

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





Слайд 88Класс 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;
} }

Слайд 89Преимущество Strategy по сравнению с Template Methоd
Template Methоd позволяет подставлять

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



Слайд 90Выводы
Паттерн Template Methоd легко использовать на практике, но он недостаточно гибок.


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




Слайд 91Паттерн Mediator (Посредник)
Например, класс QuickEntryMediator находится за сценой и привязывает текстовое

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




Слайд 92Класс 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);


Слайд 93Kласс 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(); }
} }


Слайд 94Структура класса QuickEntryMediator
Конструктору экземпляра QuickEntryMediator передаются ссылки на ListBox и TextBox.


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


Слайд 95Выводы
Накладывать политику можно
сверху, используя паттерн Фасад, если эта политика должна

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


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


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


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


Обратная связь

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

Email: Нажмите что бы посмотреть 

Что такое ThePresentation.ru?

Это сайт презентаций, докладов, проектов, шаблонов в формате PowerPoint. Мы помогаем школьникам, студентам, учителям, преподавателям хранить и обмениваться учебными материалами с другими пользователями.


Для правообладателей

Яндекс.Метрика