84
Design Patterns. Шаблоны GoF Немчинский Сергей 2008

Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Embed Size (px)

Citation preview

Page 1: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Design Patterns. Шаблоны GoF

Немчинский Сергей2008

Page 2: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Типы шаблонов

Порождающие паттерны проектирования

Структурные паттерны проектирования классов/объектов

Паттерны проектирования поведения классов/объектов

Page 3: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Порождающие паттерны (Creational Patterns) Абстрактная фабрика (Abstract

Factory, Factory), др. название Инструментарий (Kit)

Одиночка (Singleton) Прототип (Prototype) Строитель (Builder) Фабричный метод (Factory Method)

или Виртуальный конструктор (Virtual Constructor)

Page 4: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Абстрактная фабрика (Abstract Factory, Factory)

Проблема Создать семейство взаимосвязанных

или взаимозависимых объектов (не специфицируя их конкретных классов).

Решение Создать абстрактный класс, в

котором объявлен интерфейс для создания конкретных классов.

Page 5: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Абстрактная фабрика (Abstract Factory, Factory)

Page 6: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Abstract Factory. C++/* * GUIFactory example */#include <iostream>#include <string>#include <cstdlib>using namespace std; class Button{public: virtual ~Button() { } virtual void paint() = 0;}; class WinButton : public Button{public: void paint() { cout<<"I'm a WinButton:

"; }}; class OSXButton : public Button{public: void paint() { cout<<"I'm an OSXButton:

"; }};

int main(){ GUIFactory* factory =

GUIFactory::getFactory(); Button* button = factory->createButton(); button->paint(); delete factory; delete button; return 0;}// Output is either:// "I'm a WinButton:"// or:// "I'm an OSXButton:"

class GUIFactory {public: virtual ~GUIFactory() { } virtual Button* createButton() = 0; static GUIFactory* getFactory();}; class WinFactory : public GUIFactory{public: Button* createButton() { return new WinButton(); }}; class OSXFactory : public GUIFactory{public: Button* createButton() { return new OSXButton(); }};

Page 7: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Abstract Factory. Критика Преимущества

Изолирует конкретные классы. Поскольку "Абстрактная фабрика" инкапсулирует ответственность за создание классов и сам процесс их создания, то она изолирует клиента от деталей реализации классов.

Упрощена замена "Абстрактной фабрики", поскольку она используется в приложении только один раз при инстанцировании.

Недостатки Интерфейс "Абстрактной фабрики" фиксирует набор

объектов, которые можно создать. Расширение "Абстрактной фабрики" для изготовления новых объектов часто затруднительно.

Page 8: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Фабричный метод (Factory Method ) или Виртуальный конструктор (Virtual Constructor) Проблема

Определить интерфейс для создания объекта, но оставить подклассам решение о том, какой класс инстанцировать, то есть, делегировать инстанцирование подклассам.

Используется, когда: классу заранее неизвестно, объекты каких

подклассов ему нужно создавать. класс спроектирован так, чтобы объекты,

которые он создаёт, специфицировались подклассами.

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

Page 9: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Factory Method. Решение Абстрактный класс "Создатель" объявляет

ФабричныйМетод, возвращающий объект типа "Продукт" (абстрактный класс, определяющий интерфейс обьектов, создаваемых фабричным методом).

"Создатель также может определить реализацию по умолчанию ФабричногоМетода, который возвращает "КонкретныйПродукт". "КонкретныйСоздатель" замещает ФабричныйМетод, возвращающий объект "КонкретныйПродукт".

"Создатель" "полагается" на свои подклассы в определении ФабричногоМетода, возвращающего объект "КонкретныйПродукт"

Page 10: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Factory Method. Диаграмма

Page 11: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Factory Method. Примерclass Computer { public: virtual void Run() = 0; virtual void Stop() = 0; }; class Laptop: public Computer { public: virtual void Run(){mHibernating = false;} virtual void Stop(){mHibernating = true;} private: bool mHibernating; // Whether or not the machine is

hibernating }; class Desktop: public Computer { public: virtual void Run(){mOn = true;} virtual void Stop(){mOn = false;} private: bool mOn; // Whether or not the machine has been

turned on };

class ComputerFactory { public: static Computer *NewComputer(const std::string

&description) { if(description == "laptop") return new Laptop; if(description == "desktop") return new Desktop; return NULL; } };

Page 12: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Factory Method. Критика

Преимущества Избавляет проектировщика от

необходимости встраивать в код зависящие от приложения классы.

Недостатки Возникает дополнительный

уровень подклассов.

Page 13: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Структурные паттерны Адаптер (Adapter) Декоратор (Decorator) или Оболочка

(Wrapper) Заместитель (Proxy) или Суррогат

(Surrogate) Компоновщик (Composite) Мост (Bridge), Handle (описатель) или

Тело (Body) Приспособленец (Flyweight) Фасад (Facade)

Page 14: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Адаптер (Adapter) Проблема

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

Решение Конвертировать исходный интерфейс

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

Page 15: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Adapter. Диаграмма

Page 16: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Adapter. Пример// Purpose. Adapter design pattern demo// // Discussion. LegacyRectangle's interface is not compatible with the// system that would like to reuse it. An abstract base class is created// that specifies the desired interface. An "adapter" class is defined// that publicly inherits the interface of the abstract class, and// privately inherits the implementation of the legacy component. This// adapter class "maps" or "impedance matches" the new interface to the// old implementation.

#include <iostream.h>

typedef int Coordinate;typedef int Dimension;

/////////////////////////// Desired interface ///////////////////////////class Rectangle {public: virtual void draw() = 0;};

/////////////////////////// Legacy component ///////////////////////////class LegacyRectangle {public: LegacyRectangle( Coordinate x1, Coordinate y1, Coordinate x2, Coordinate y2 ) { x1_ = x1; y1_ = y1; x2_ = x2; y2_ = y2; cout << "LegacyRectangle: create. (" << x1_ << "," << y1_ << ") => (" << x2_ << "," << y2_ << ")" << endl; } void oldDraw() { cout << "LegacyRectangle: oldDraw. (" << x1_ << "," << y1_ << ") => (" << x2_ << "," << y2_ << ")" << endl; }private: Coordinate x1_; Coordinate y1_; Coordinate x2_; Coordinate y2_;};

/////////////////////////// Adapter wrapper ///////////////////////////class RectangleAdapter : public Rectangle, private LegacyRectangle {public: RectangleAdapter( Coordinate x, Coordinate y, Dimension w,

Dimension h ) : LegacyRectangle( x, y, x+w, y+h ) { cout << "RectangleAdapter: create. (" << x << "," << y << "), width = " << w << ", height = " << h << endl; } virtual void draw() { cout << "RectangleAdapter: draw." << endl; oldDraw(); }};

void main() { Rectangle* r = new RectangleAdapter( 120, 200, 60, 40 ); r->draw();}

// LegacyRectangle: create. (120,200) => (180,240)// RectangleAdapter: create. (120,200), width = 60, height = 40// RectangleAdapter: draw.// LegacyRectangle: oldDraw. (120,200) => (180,240)

Page 17: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Adapter Применяется в случаях

система поддерживает требуемые данные и поведение, но имеет неподходящий интерфейс.

Плюсы инкапсуляция реализации внешних классов

(компонентов, библиотек), система становится независимой от интерфейса внешних классов;

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

Page 18: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Декоратор (Decorator) или Оболочка (Wrapper) Проблема

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

Рекомендации Применение нескольких "Декораторов" к

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

Page 19: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Decorator. Решение Динамически добавить объекту новые обязанности

не прибегая при этом к порождению подклассов (наследованию).

"Компонент"определяет интерфейс для обьектов, на которые могут быть динамически возложены дополнительные обязанности, "КонкретныйКомпонент" определяет объект, на который возлагаются дополнительные обязанности, "Декоратор" - хранит ссылку на объект "Компонент" и определяет интерфейс, соответствующий интерфейсу "Компонента". "КонкретныйДекоратор" возлагает дополнительные обязанности на компонент.

"Декоратор" переадресует запросы объекту "Компонент".

Page 20: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Decorator. Диаграмма

Page 21: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Decorator. Второй пример

Page 22: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Decorator. Пример кода#include <iostream> using namespace std; /* Component (interface) */class Widget { public: virtual void draw() = 0; }; /* ConcreteComponent */class TextField : public Widget { private: int width, height; public: TextField( int w, int h ){ width = w; height = h; } void draw() { cout << "TextField: " << width << ",

" << height << '\n'; }};

/* Decoder (interface)*/

class Decorator : public Widget { private: Widget* wid; // reference to Widget public: Decorator( Widget* w ) { wid = w; } void draw() { wid->draw(); }}; /* ConcreteDecoderA */class BorderDecorator : public Decorator { public: BorderDecorator( Widget* w ) : Decorator(

w ) { } void draw() { Decorator::draw(); cout << " BorderDecorator" << '\n'; } };

/* ConcreteDecoderB */class ScrollDecorator : public Decorator { public: ScrollDecorator( Widget* w ) :

Decorator( w ) { } void draw() { Decorator::draw(); cout << " ScrollDecorator" << '\n'; } }; void main( void ) { Widget* aWidget = new

BorderDecorator( new BorderDecorator( new ScrollDecorator( new TextField( 80,

24 )))); aWidget->draw();}

Page 23: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Decorator. Пример

На выходе программа напечатает: TextField: <width> , <height> ScrollDecorator

BorderDecorator . Обратите внимание, что метод

getDescription обоих декораторов сначала вызывает getDescription декорируемого окна, а затем добавляет туда суффикс.

Page 24: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Decorator. Критика Преимущества

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

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

Недостатки "Декоратор" и его "Компонент" не идентичны, и, кроме

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

Page 25: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Заместитель (Proxy) или Суррогат (Surrogate)

Проблема Необходимо управлять доступом к

объекту, так чтобы создавать громоздкие объекты "по требованию".

Page 26: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Заместитель (Proxy) Решение

Создать суррогат громоздкого объекта. "Заместитель" хранит ссылку, которая позволяет заместителю обратиться к реальному субъекту (объект класса "Заместитель" может обращаться к объекту класса "Субъект", если интерфейсы "РеальногоСубъекта" и "Субъекта" одинаковы).

Поскольку интерфейс "РеальногоСубъекта" идентичен интерфейсу "Субъекта", так, что "Заместителя" можно подставить вместо "РеальногоСубъекта", контролирует доступ к "РеальномуСубъекту", может отвечать за создание или удаление "РеальногоСубъекта". "Субъект" определяет общий для "РеальногоСубъекта" и "Заместителя" интерфейс, так, что "Заместитель" может быть использован везде, где ожидается "РеальныйСубъект".

При необходимости запросы могут быть переадресованы "Заместителем" "РеальномуСубъекту".

Page 27: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Заместитель (Proxy)

Page 28: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Заместитель (Proxy) "Заместитель" может иметь и другие

обязанности, а именно: удаленный "Заместитель" может отвечать

за кодирование запроса и его аргументов и отправку закодированного запроса реальному "Субъекту",

виртуальный "Заместитель" может кэшировать дополнительную информацию о реальном "Субъекте", чтобы отложить его создание,

защищающий "Заместитель" может проверять, имеет ли вызывающий объект необходимые для выполнения запроса права.

Page 29: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Компоновщик (Composite) Проблема

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

Решение Определить классы для

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

Page 30: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Composite. Диаграмма

Page 31: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Composite. Example// file graphic.h starting here #include

<iostream.h>class Composite;class Graphic {friend class Composite; Graphic *next; virtual int pictureDetect(){return(O);}public: virtual void draw(){} Graphic(){next=NULL;}};class Line : public Graphic { int xl,yl,x2,y2; public: virtual void draw(void)

{cout<<"Line:"<<xl<<yl<<x2<<y2<<'\n';)

Line(int Xl,int Yl,int X2,int Y2){xl=X1; yl=Y1; x2=X2; y2=Y2;}

};class Text : public Graphic { int x,y; char *text;public: virtual void draw(void){cout<<"Text:g

<<X<<y<< text<<'\n';} Text(int X,int Y. char *tx){x=X; y=Y; text=tx;}};class Picture : public Graphic {friend class Composite; Graphic *first; virtual int pictureDetect(){return(1);}public: virtual void draw(void){cout<<"Picture:\nN;} Picture(){first=NULL;}

void Composite::dissolve(Graphic *g){ Graphic *t,*tn;

if(g->pictureDetect(){ for(t=((Picture *)g)->first; t; t=tn){ tn=t->next; t->next=NULL; } ((Picture *)g)->first=NULL; }}

int main(void){ Line *nl,*n2,*n3,*n4; Text *tl,*t2,*t3 Picture *pl,*p2,*p3; nl=new Line(1,1,11,11); n2=new

Line(2,2,22,22); n3=new Line(3,3,33,33); n4=new

Line(4,4,44,44); tl=new Text(l,l,"one"); t2=new

Text(2,2,"two"); t3=new Text(3,3,"three"); pl=new Picture; p2=new Picture; p3=new

Picture; Composite comp;

comp.add(pl,nl); comp.add(pl,n2); comp.add(p2,n3);

comp.add(p2,n4); comp.add(p3,tl); comp.add(p3,t2);

comp.add(p2,t3); comp.add(p2,pl); comp.add(p3,p2);

comp.remove(p2,n4); comp.add(p3,n4);

comp.draw(p3); comp.dissolve(p2); cout<<'\n'; comp.draw(p3); return(O);}

class Composite {public: void add(Picture *p, Graphic *g){g->next=p-

>first; p->first=g;} void remove(Picture *p, Graphic *g); void draw(Graphic *g); void dissolve(Graphic *g);};void Composite::remove(Picture *p, Graphic *g)

{ Graphic *t; if(p->first==g){p->first=g->next; g-

>next=NULL;} else { for(t=p->first; t; t=t->next) if(t-

>next==g)break; if(t){t->next=g->next; g->next=NULL;} else cout<<"error\n"; }}

void Composite::draw(Graphic *g){ static int level=O; // not necessary, just for

better // displays int i; Graphic *t;

for(i=O;ilevel;i++)cout<<" "; // just to indent the

// print g->draw(); if(g->pictureDetect()){ level++; for(t=((Picture *)g)->first; t; t=t->next)

draw(t); level-; }}

Page 32: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Мост (Bridge), Handle (описатель) или Тело (Body) Проблема

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

Решение Поместить абстракцию и реализацию в

отдельные иерархии классов.

Page 33: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Bridge. Диаграмма

Page 34: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Bridge. Примерusing namespace std; /********************************************//* Implementor

*//********************************************/// interfaceclass DrawingAPI {public: virtual void drawCircle(double x, double y,

double radius) = 0;}; //concrete implementor1class DrawingAPI1 : public DrawingAPI{public: void drawCircle(double x, double y, double

radius) { cout<<"API1.circle

at"<<x<<":"<<y<<" "<< radius<<endl; }}; //concrete implementor2class DrawingAPI2 : public DrawingAPI{public: void drawCircle(double x, double y, double

radius) { cout<<"API2.circle at

"<<x<<":"<<y<<" "<< radius<<endl; }};

/**************************************//* Abstraction

*//**************************************/class Shape{public: virtual void draw() = 0; virtual void

resizeByPercentage(double pct) = 0;}; class CircleShape:public Shape{public: CircleShape(double x, double y,double

radius,DrawingAPI &drawingAPI):

m_x(x),m_y(y),m_radius(radius),m_drawingAPI(drawingAPI)

{} void draw() {

m_drawingAPI.drawCircle(m_x,m_y,m_radius);

} void resizeByPercentage(double pct) { m_radius *= pct; }private: double m_x,m_y,m_radius; DrawingAPI& m_drawingAPI;};

//////////////////////////////////////////Testtypedef std::vector<Shape*>::iterator

ShapeIt;int main(int argc, char* argv[]){ std::vector<Shape*> vecShapes; vecShapes.push_back(new

CircleShape(1,2,3,*(new DrawingAPI1)));

vecShapes.push_back(new CircleShape(5,7,11,*(new DrawingAPI2)));

ShapeIt begin,end; begin = vecShapes.begin(); end = vecShapes.end();

for_each(begin,end,std::bind2nd(std::mem_fun(&Shape::resizeByPercentage),2.5));

for_each(begin,end,mem_fun(&Shape::draw));

return 0;}

Page 35: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Bridge. Пример

Page 36: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Bridge. Преимущества Отделение реализации от интерфейса, то

есть, "Реализацию" "Абстракции" можно конфигурировать во время выполнения.

Разделение классов "Абстракция" и "Реализация" устраняет зависимости от реализации, устанавливаемые на этапе компиляции: чтобы изменить класс "Реализация" вовсе не обязательно перекомпилировать класс "Абстракция".

Page 37: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Фасад (Facade)

Проблема Как обеспечить унифицированный

интерфейс с набором разрозненных реализаций или интерфейсов, например, с подсистемой, если нежелательно высокое связывание с этой подсистемой или реализация подсистемы может измениться?

Page 38: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Фасад (Facade) Решение

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

Фасад - это внешний объект, обеспечивающий единственную точку входа для служб подсистемы.

Реализация других компонентов подсистемы закрыта и не видна внешним компонентам.

Фасадный объект обеспечивает реализацию паттерна «Устойчивый к изменениям» (Protected Variations) с точки зрения защиты от изменений в реализации подсистемы.

Page 39: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Фасад (Facade). Диаграмма

Page 40: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Поведенческие шаблоны (Behavioral) Интерпретатор (Interpreter) Итератор (Iterator) или Курсор (Cursor) Команда (Command), Действие (Action) или

Транзакция (Транзакция) Наблюдатель (Observer), Опубликовать -

подписаться (Publish - Subscribe) или Delegation Event Model

Посетитель (Visitor) Посредник (Mediator) Состояние (State) Стратегия (Strategy) Хранитель (Memento) Цепочка обязанностей (Chain of Responsibility) Шаблонный метод (Template Method)

Page 41: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Итератор (Iterator) или Курсор (Cursor)

Проблема Составной объект, например,

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

Page 42: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Итератор (Iterator). Решение

Создается класс "Итератор", который определяет интерфейс для доступа и перебора элементов, "КонкретныйИтератор" реализует интерфейс класса "Итератор" и следит за текущей позицией при обходе "Агрегата".

"Агрегат" определяет интерфейс для создания объекта - итератора.

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

Page 43: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Итератор (Iterator). Диаграмма

Page 44: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Итератор (Iterator).class Stack

{ int items[10]; int sp; public: friend class StackIter; Stack() { sp = - 1; } void push(int in) { items[++sp] = in; } int pop() { return items[sp--]; } bool isEmpty() { return (sp == - 1); } StackIter

*createIterator()const; // 2. Add a createIterator() member

};

class StackIter{ // 1. Design an "iterator"

class const Stack *stk; int index; public: StackIter(const Stack *s) { stk = s; } void first() { index = 0; } void next() { index++; } bool isDone() { return index == stk->sp

+ 1; } int currentItem() { return stk->items[index]; }};

StackIter *Stack::createIterator()const

{ return new StackIter(this);} bool operator == (const Stack &l,

const Stack &r){ // 3. Clients ask the container

object to create an iterator object

StackIter *itl = l.createIterator(); StackIter *itr = r.createIterator(); // 4. Clients use the first(), isDone(),

next(), and currentItem() protocol

for (itl->first(), itr->first(); !itl->isDone();itl->next(), itr->next())

if (itl->currentItem() != itr->currentItem())

break; bool ans = itl->isDone() && itr-

>isDone(); delete itl; delete itr; return ans;}

int main(){ Stack s1; for (int i = 1; i < 5; i++) s1.push(i); Stack s2(s1), s3(s1), s4(s1),

s5(s1); s3.pop(); s5.pop(); s4.push(2); s5.push(9); cout << "1 == 2 is " << (s1

== s2) << endl; cout << "1 == 3 is " << (s1

== s3) << endl; cout << "1 == 4 is " << (s1

== s4) << endl; cout << "1 == 5 is " << (s1

== s5) << endl;}

Page 45: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Итератор (Iterator). Преимущества

Поддерживает различные способы перебора агрегата,

одновременно могут быть активны несколько переборов.

Page 46: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Команда (Command), Действие (Action) или Транзакция (Транзакция)

Проблема Необходимо послать объекту

запрос, не зная о том, выполнение какой операции запрошено и кто будет получателем.

Page 47: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Команда (Command). Решение Инкапсулировать запрос как объект. "Клиент" создает объект "КонкретнаяКоманда",

который вызывает операции получателя для выполнения запроса, "Инициатор" отправляет запрос, выполняя операцию "Команды" Выполнить().

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

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

Page 48: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Команда (Command). Диаграмма

Page 49: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Команда (Command). Пример#include <iostream> #include

<string> using namespace std;class Person;

class Command{ // 1. Create a class that encapsulates

an object Person *object; // and a member

function void(Person:: *method)(); // a pointer

to a member function (the attri- public: // bute's name is "method") Command(Person *obj = 0,

void(Person:: *meth)() = 0) { object = obj; // the argument's

name is "meth" method = meth; } void execute() { (object-> *method)(); // invoke the

method on the object }};

class Person{ string name; Command cmd; // cmd is a "black box", it is a

method invoca- public: // tion promoted to "full object status" Person(string n, Command c): cmd(c) { name = n; } void talk() { // "this" is the sender, cmd has the

receiver cout << name << " is talking" << endl; cmd.execute(); // ask the "black box" to

callback the receiver } void passOn() { cout << name << " is passing on" <<

endl; cmd.execute(); // 4. When the sender is

ready to callback to } // the receiver, it calls execute() void gossip() { cout << name << " is gossiping" << endl; cmd.execute(); } void listen() { cout << name << " is listening" << endl; }};

int main(){ // Fred will "execute" Barney which will result

in a call to passOn() // Barney will "execute" Betty which will

result in a call to gossip() // Betty will "execute" Wilma which will result

in a call to listen() Person wilma("Wilma", Command()); // 2. Instantiate an object for each "callback" // 3. Pass each object to its future "sender" Person betty("Betty", Command(&wilma,

Person::listen)); Person barney("Barney", Command(&betty,

&Person::gossip)); Person fred("Fred", Command(&barney,

&Person::passOn)); fred.talk();}

Page 50: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Команда (Command). Преимущества

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

Page 51: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Наблюдатель (Observer), Опубликовать - подписаться (Publish - Subscribe) или Listener

Проблема Один объект ("Подписчик") должен знать об

изменении состояний или некоторых событиях другого объекта. При этом необходимо поддерживать низкий уровень связывания с объектом - "Подписчиком".

Решение Определить интерфейс "Подписки". Объекты -

подписчики реализуют этот интерфейс и динамически регистрируются для получении информации о некотором событии. Затем при реализации условленного события оповещаются все объекты - подписчики.

Page 52: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Observer

Page 53: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Observer. Область применения Шаблон «наблюдатель» применяется в тех

случаях, когда система обладает следующими свойствами:

существует, как минимум, один объект, рассылающий сообщения

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

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

Page 54: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Посетитель (Visitor).

Проблема Над каждым объектом некоторой

структуры выполняется операция. Определить новую операцию, не изменяя классы объектов.

Page 55: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Посетитель (Visitor). Клиент, использующий данный паттерн, должен

создать объект класса "КонкретныйПосетитель", а затем посетить каждый элемент структуры.

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

"КонкретныйПосетитель" реализует все операции, обьявленные в классе "Посетитель".

Каждая операция реализует фрагмент алгоритма, определенного для класса соответствующего объекта в структуре.

Page 56: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Посетитель (Visitor). Класс

"КонкретныйПосетитель"предоставляет контекст для этого алгоритма и сохраняет его локальное состояние.

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

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

Page 57: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Посетитель (Visitor).

Page 58: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Посетитель (Visitor).#include <iostream>#include <memory>#include <vector>#include <boost/shared_ptr.hpp>

template <class T>class TVisitor{public: virtual ~TVisitor() {} virtual void Accept(T& t) = 0;};class Fir;class Pine;class Tree{public: virtual ~Tree() {} virtual void Visit(TVisitor<Fir>& v)

{} virtual void Visit(TVisitor<Pine>& v)

{}};

typedef boost::shared_ptr<Tree> TreePtr;

class Fir : public Tree{public:

virtual void Visit(TVisitor<Fir>& v) { v.Accept(*this); }};

class Pine : public Tree{public:

virtual void Visit(TVisitor<Pine>& v) { v.Accept(*this); }};

class SawFirVisitor : public TVisitor<Fir>{public:

virtual void Accept(Fir& fir) { std::cout << "Fir sawed!" <<

std::endl; }};

class DecorateFirVisitor : public TVisitor<Fir>

{public:

virtual void Accept(Fir& fir) { std::cout << "Fir decorated!" <<

std::endl; }};

int main(){ typedef std::vector<TreePtr> Forest; Forest forest;

forest.push_back(TreePtr(new Fir)); forest.push_back(TreePtr(new Pine)); forest.push_back(TreePtr(new Pine)); forest.push_back(TreePtr(new Fir)); forest.push_back(TreePtr(new Fir));

SawFirVisitor sawFir; DecorateFirVisitor decorateFir;

for(Forest::iterator i = forest.begin(); i != forest.end(); ++i)

{ (*i)->Visit(sawFir); (*i)->Visit(decorateFir); }}

Page 59: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Посетитель (Visitor). Рекомендации

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

Преимущества Упрощается добавление новых операций, объединяет

родственные операции в классе "Посетитель". Недостатки

Затруднено добавление новых классов "КонкретныйЭлемент", поскольку требуется объявление новой абстрактной операции в классе "Посетитель".

Page 60: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Состояние (State) Проблема

Варьировать поведение объекта в зависимости от его внутреннего состояния

Решение Класс "Контекст" делегирует зависящие от

состояния запросы текущему объекту "КонкретноеСостояние" (хранит экземпляр подкласса "КонкретноеСостояние", которым определяется текущее состояние), и определяет интерфейс, представляющий интерес для клиентов.

"КонкретноеСостояние" реализует поведение, ассоциированное с неким состоянием объекта "Контекст". "Состояние" определяет интерфейс для инкапсуляции поведения, ассоциированного с конкретным экземпляром "Контекста".

Page 61: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Состояние (State)

Page 62: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Состояние (State)class AbstractTool { public: virtual void MoveTo(const Point& inP) = 0; virtual void MouseDown(const Point& inP) =

0; virtual void MouseUp(const Point& inP) = 0; };

class PenTool : public AbstractTool { public: PenTool() : mMouseIsDown(false) {} virtual void MoveTo(const Point& inP) { if(mMouseIsDown) { DrawLine(mLastP, inP); } mLastP = inP; } virtual void MouseDown(const Point&

inP) { mMouseIsDown = true; mLastP = inP; } virtual void MouseUp(const Point& inP)

{ mMouseIsDown = false; } private: bool mMouseIsDown; Point mLastP; };

class SelectionTool : public AbstractTool {

public: SelectionTool() :

mMouseIsDown(false) {} virtual void MoveTo(const Point&

inP) { if(mMouseIsDown) { mSelection.Set(mLastP, inP); } } virtual void MouseDown(const

Point& inP) { mMouseIsDown = true; mLastP = inP; mSelection.Set(mLastP, inP); } virtual void MouseUp(const Point&

inP) { mMouseIsDown = false; } private: bool mMouseIsDown; Point mLastP; Rectangle mSelection; };

class DrawingController { public: DrawingController()

{ selectPenTool(); } // Start with some tool.

void MoveTo(const Point& inP) {currentTool->MoveTo(inP)}

void MouseDown(const Point& inP) {currentTool->MouseDown(inP)}

void MouseUp(const Point& inP) {currentTool->MouseUp(inP)}

selectPenTool() { currentTool.reset(new PenTool); } selectSelectionTool() { currentTool.reset(new

SelectionTool); } private: std::auto_ptr<AbstractTool>

currentTool; };

Page 63: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Состояние (State). Преимущества

Локализует зависящее от состояния поведение и делит его на части, соответствующие состояниям,

переходы между состояниями становятся явными.

Page 64: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Стратегия (Strategy)

Проблема Спроектировать изменяемые, но

надежные алгоритмы или стратегии.

Решение Определить для каждого алгоритма

или стратегии отдельный класс со стандартным интерфейсом.

Page 65: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Стратегия (Strategy). Пример Обеспечение сложной логики вычисления

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

Создается несколько классов "Стратегия", каждый из которых содержит один и тот же полиморфный метод "ЦенаРассчитать". В качестве параметров в этот метод передаются данные о продаже. Объект стратегии связывается с контекстным объектом (тем объектом, к которому применяется алгоритм).

Page 66: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Стратегия (Strategy).

Page 67: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Стратегия (Strategy).#include <iostream.h>#include <fstream.h>#include <string.h>

class Strategy;

class TestBed {public: enum StrategyType { Dummy, Left, Right,

Center }; TestBed() { strategy_ = NULL; } void setStrategy( int type, int width ); void doIt();private: Strategy* strategy_;};class Strategy {public: Strategy( int width ) : width_( width ) { } void format() { char line[80], word[30]; ifstream inFile( "quote.txt", ios::in ); line[0] = '\0'; inFile >> word; strcat( line, word ); while (inFile >> word) { if (strlen(line) + strlen(word) + 1 > width_) justify( line ); else strcat( line, " " ); strcat( line, word ); } justify( line ); }protected: int width_;private: virtual void justify( char* line ) = 0;};

class LeftStrategy : public Strategy {public: LeftStrategy( int width ) : Strategy( width ) { }private: /* virtual */ void justify( char* line ) { cout << line << endl; line[0] = '\0'; }};

class RightStrategy : public Strategy {public: RightStrategy( int width ) : Strategy( width )

{ }private: /* virtual */ void justify( char* line ) { char buf[80]; int offset = width_ - strlen( line ); memset( buf, ' ', 80 ); strcpy( &(buf[offset]), line ); cout << buf << endl; line[0] = '\0'; }};

class CenterStrategy : public Strategy {public: CenterStrategy( int width ) : Strategy( width ) {

}private: /* virtual */ void justify( char* line ) { char buf[80]; int offset = (width_ - strlen( line )) / 2; memset( buf, ' ', 80 ); strcpy( &(buf[offset]), line ); cout << buf << endl; line[0] = '\0'; }};

class LeftStrategy : public Strategy {public: LeftStrategy( int width ) : Strategy( width ) { }private: /* virtual */ void justify( char* line ) { cout << line << endl; line[0] = '\0'; }};

class RightStrategy : public Strategy {public: RightStrategy( int width ) : Strategy( width ) { }private: /* virtual */ void justify( char* line ) { char buf[80]; int offset = width_ - strlen( line ); memset( buf, ' ', 80 ); strcpy( &(buf[offset]), line ); cout << buf << endl; line[0] = '\0'; }};

class CenterStrategy : public Strategy {public: CenterStrategy( int width ) : Strategy( width ) { }private: /* virtual */ void justify( char* line ) { char buf[80]; int offset = (width_ - strlen( line )) / 2; memset( buf, ' ', 80 ); strcpy( &(buf[offset]), line ); cout << buf << endl; line[0] = '\0'; }};

Page 68: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Стратегия (Strategy). Плюсы

инкапсуляция реализации различных алгоритмов, система становится независимой от возможных изменений бизнес-правил;

вызов всех алгоритмов одним стандартным образом;

отказ от использования переключателей и/или условных операторов.

Минусы создание дополнительных классов

Page 69: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Цепочка обязанностей (Chain of Responsibility) Проблема

Запрос должен быть обработан несколькими объектами.

Рекомендации Логично использовать данный паттерн,

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

Page 70: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Цепочка обязанностей Шаблон рекомендован для

использования в условиях: в разрабатываемой системе имеется группа

объектов, которые могут обрабатывать сообщения определенного типа;

все сообщения должны быть обработаны хотя бы одним объектом системы;

сообщения в системе обрабатываются по схеме «обработай сам либо перешли другому», то есть одни сообщения обрабатываются на том уровне, где они получены, а другие пересылаются объектам иного уровня.

Page 71: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Цепочка обязанностей

Page 72: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Цепочка обязанностей Связать объекты - получатели запроса в цепочку и

передать запрос вдоль этой цепочки, пока он не будет обработан.

"Обработчик" определяет интерфейс для обработки запросов, и, возможно, реализует связь с преемником,

Page 73: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Цепочка обязанностей#include <iostream>#include <vector>#include <ctime>using namespace std;

class Base { Base* next; // 1. "next" pointer in the base

classpublic: Base() { next = 0; } void setNext( Base* n ) { next = n; } void add( Base* n ) { if (next) next->add( n ); else

next = n; } // 2. The "chain" method in the base class always

delegates to the next obj virtual void handle( int i ) { next->handle( i ); }};

class Handler1 : public Base { public: /*virtual*/ void handle( int i ) { if (rand() % 3) { // 3. Don't handle requests 3

times out of 4 cout << "H1 passsed " << i << " "; Base::handle( i ); // 3. Delegate to the base class } else cout << "H1 handled " << i << " ";} };

class Handler2 : public Base { public: /*virtual*/ void handle( int i ) { if (rand() % 3) { cout << "H2 passsed " << i << " ";

Base::handle( i ); } else cout << "H2 handled " << i << " ";} };

class Handler3 : public Base { public: /*virtual*/ void handle( int i ) { if (rand() % 3) { cout << "H3 passsed " << i << " ";

Base::handle( i ); } else cout << "H3 handled " << i << " ";} };

void main( void ) { srand( time( 0 ) ); Handler1 root; Handler2 two; Handler3 thr; root.add( &two ); root.add( &thr ); thr.setNext( &root ); for (int i=1; i < 10; i++) { root.handle( i ); cout << '\n';} }

Page 74: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Цепочка обязанностей

Преимущества Ослабляется связанность (объект

не обязан "знать", кто именно обработает его запрос).

Недостатки Нет гарантий, что запрос будет

обработан, поскольку он не имеет явного получателя.

Page 75: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Шаблонный метод (Template Method)

Проблема Определить алгоритм и

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

Page 76: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Шаблонный метод (Template Method) Решение

"АбстрактныйКласс" определяет абстрактные Операции(), замещаемые в конкретных подклассах для реализации шагов алгоритма, и реализует ШаблонныйМетод(), определяющий "скелет" алгоритма. "КонкретныйКласс" релизует Операции(), выполняющие шаги алгоритма способом, который зависит от подкласса. "КонкретныйКласс" предполагает, что инвариантные шаги алгоритма будут выполнены в "АбстрактномКлассе".

Page 77: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Шаблонный метод (Template Method)

Page 78: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Шаблонный метод (Template Method)#include <iostream>using namespace std;

class Base { void a() { cout << "a "; } void c() { cout << "c "; } void e() { cout << "e "; } // 2. Steps requiring peculiar

implementations are "placeholders" in base class

virtual void ph1() = 0; virtual void ph2() = 0;public: // 1. Standardize the skeleton of an

algorithm in a base class "template method"

void execute() { a(); ph1(); c(); ph2(); e(); }

};

class One : public Base { // 3. Derived classes implement

placeholder methods /*virtual*/ void ph1() { cout << "b "; } /*virtual*/ void ph2() { cout << "d "; }};

class Two : public Base { /*virtual*/ void ph1() { cout << "2 "; } /*virtual*/ void ph2() { cout << "4 "; }};

void main( void ) { Base* array[] = { &One(), &Two() }; for (int i=0; i < 2; i++) { array[i]->execute(); cout << '\n';} }

Page 79: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Model-view-controller Model-view-controller (MVC, «Модель-

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

Page 80: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Model-view-controller Шаблон MVC позволяет разделить данные,

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

Модель (Model). Модель предоставляет данные (обычно для View), а также реагирует на запросы (обычно от контролера), изменяя свое состояние .

Представление (View). Отвечает за отображение информации (пользовательский интерфейс).

Поведение (Controller). Интерпретирует данные, введенные пользователем, и информирует модель и представление о необходимости соответствующей реакции.

Page 81: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Model-view-controller

Page 82: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

MVC

Page 83: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Model-view-controller Важно отметить, что как представление,

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

Впервые данный шаблон проектирования был предложен для языка Smalltalk.

Page 84: Шаблоны разработки ПО. Часть 3. Шаблоны GoF

Итоги

Типы шаблонов Порождающие паттерны

проектирования Структурные паттерны

проектирования классов/объектов Паттерны проектирования

поведения классов/объектов