Модуль 4: Дополнительные темы объектно-ориентированного
программирования.
Темы лекции: Отношения между классами. Порождающие
шаблоны проектирования.
Практическое задание: Порождающие шаблоны.
Тренер: Игорь Шкулипа, к.т.н.
C++ Базовый. Занятие 16
http://www.slideshare.net/IgorShkulipa 2
Отношения между классами в ООП
http://www.slideshare.net/IgorShkulipa 3
Ассоциация
Ассоциация - самый слабый вид связи. Обычно ассоциация
возникает, когда один класс вызывает метод другого или
если при вызове метода в качестве аргумента передаётся
объект другого класса.
http://www.slideshare.net/IgorShkulipa 4
Агрегация
Агрегация (присоединение, включение) - агрегирование или
соединение частей. Например, когда класс содержит поле –
объект другого класса.
http://www.slideshare.net/IgorShkulipa 5
Композиция
Композиция классов - более сильная связь между классами, чем
агрегация.
Между агрегацией и композицией довольно тонкая грань.
Особенностью композиции является то, что объекты, из
которых создаётся композиция, могут принадлежать
только классу, с которым они образуют композицию.
При этом время жизни объекта и класса, в который
встраивается объект, совпадает.
http://www.slideshare.net/IgorShkulipa 6
Наследование
Наследование – это отношение между классами, при котором
класс-наследник использует поля и методы родительского
класса.
Класс, структура и поведение которого наследуется, называется
суперклассом, надклассом, базовым или родительским
классом
Класс, производный от суперкласса, называется подклассом,
производным или дочерним классом
http://www.slideshare.net/IgorShkulipa 7
Формы наследования
В подклассе структура и поведение исходного суперкласса могут
дополняться, переопределяться или ограничиваться.
Исходя из этого, можно выделить различные формы
наследования:
1. Специализация
2. Спецификация
3. Обобщение
4. Расширение
5. Ограничение
6. Конструирование
7. Варьирование
8. Комбинирование
http://www.slideshare.net/IgorShkulipa 8
Специализация
Дочерний класс является более конкретным, частным или
специализированным случаем родительского класса.
Дочерний класс удовлетворяет спецификациям родителя во
всех существенных моментах, т.е. его можно использовать
вместо родительского класса.
Поведение базового класса, в основном, переопределяется.
специализация
http://www.slideshare.net/IgorShkulipa 9
Спецификация
Родительский класс описывает поведение, которое реализуется в
дочернем классе, но оставлено нереализованным в
родительском
В таких случаях родительский класс называют абстрактно-
специфицированным классом. Пример: интерфейсы.
спецификацияспецификацияспецификация
http://www.slideshare.net/IgorShkulipa 10
Обобщение
Дочерний класс модифицирует и переопределяет некоторые
методы родительского класса с целью получения объекта
более общей категории.
Данная форма наследования противоположна специализации и
обычно применяется, когда построение происходит на основе
существующих классов, которые мы не хотим или не можем
изменять. Пример: Массив и контейнер.
обобщение
http://www.slideshare.net/IgorShkulipa 11
Расширение
Дочерний класс добавляет новые функциональные возможности
к родительскому классу, но не меняет наследуемое
поведение.
В отличие от обобщения или специализации при расширении
дочерний класс не переопределяет ни одного метода базового
класса, а добавленные методы слабо связаны с
существующими методами родителя.
расширение
http://www.slideshare.net/IgorShkulipa 12
Ограничение
Дочерний класс ограничивает использование некоторых методов
родительского класса.
Так же, как и при обобщении, применение для ограничения чаще
всего возникает, когда, класс строится на основе
существующей иерархии классов, которая не должна или не
может быть изменена. Пример: Список, стек, очередь
ограничениеограничение
http://www.slideshare.net/IgorShkulipa 13
Конструирование
Дочерний класс использует методы, поставляемые родительским
классом.
Между дочерним и родительским классами отсутствует отношение
«is-a» или «быть экземпляром», то есть дочерний класс не является
более специализированной формой родительского класса.
Обычно для реализации такой формы наследования используется
механизм закрытого наследования.
Дочерний класс часто изменяет не только имена методов базового
класса, но и аргументы.
конструирование
http://www.slideshare.net/IgorShkulipa 14
Варьирование
Варьирование наблюдается, когда два класса имеют сходную
реализацию и находятся примерно на одном уровне иерархии,
то есть являются частными случаями более общего понятия.
варьирование
http://www.slideshare.net/IgorShkulipa 15
Комбинирование
Дочерний класс наследует черты более чем одного
родительского класса.
Для комбинирования классов используется механизм
множественного наследования.
http://www.slideshare.net/IgorShkulipa 16
Правильные формы наследования (is-a)
К правильным формам относятся только те, в которых между
дочерним и родительским классом возникает отношение "is-
a" или «быть экземпляром», то есть дочерний класс является
частным случаем своего предка.
Если B не есть A, то B не стоит наследовать от A.
Пример: Прямоугольник не является частным случаем квадрата,
по этому, класс Rectangle не стоит наследовать от класса
Square.
Правильные формы наследования is-a:
⚫ Специализация
⚫ Спецификация
⚫ Расширение (в большинстве случаев)
⚫ Ограничение (иногда)
⚫ Комбинирование (иногда)
http://www.slideshare.net/IgorShkulipa 17
Правильные формы наследования (тип-подтип)
Для того чтобы класс B был подтипом класса A, он должен
иметь схожее поведение с классом A во всех существенных
моментах.
Схожее поведение означает, что класс B должен поддерживать
тот же набор методов, что и класс A.
Пример: Базовый: Геометрическая фигура. Наследники:
Окружность, Треугольник, Прямоугольник и т.д.
Правильные формы наследования "тип−подтип“:
⚫ Специализация
⚫ Спецификация
⚫ Обобщение
⚫ Расширение
⚫ Конструирование (очень редко)
⚫ Варьирование (иногда)
⚫ Комбинирование
http://www.slideshare.net/IgorShkulipa 18
Формы наследования
Абсолютно правильные формы наследования:
⚫ Специализация
⚫ Спецификация
⚫ Расширение - если производный класс есть частный случай
базового
⚫ Комбинирование- если производный класс есть частный случай
базовых и является подтипом базовых типов
Допустимые формы наследования:
⚫ Обобщение
⚫ Ограничение
⚫ Варьирование
⚫ Комбинирование - если производный класс не является частным
случаем базовых
Недопустимые формы наследования:
⚫ Конструирование всегда является недопустимой формой
наследования и всегда может быть заменено другими формами
наследования и отношениями между классами.
http://www.slideshare.net/IgorShkulipa 19
Порождающие шаблоны проектирования
http://www.slideshare.net/IgorShkulipa 20
Паттерны (шаблоны проектирования)
Паттерн описывает задачу, которая снова и снова возникает в работе, а так же
принцип ее решения, причем таким образом, что это решение можно потом
использовать много раз, ничего не изобретая заново.
В общем случае паттерн состоит из четырех основных элементов:
Имя. Присваивание паттернам имен позволяет проектировать на более высоком
уровне абстракции. С помощью имен паттернов можно вести общение с
коллегами. Назначение паттернам имен упрощает общение в профессиональной
среде.
Задача - это описание того, когда следует применять паттерн. Необходимо
сформулировать задачу и ее контекст. Может описываться конкретная проблема
проектирования, например способ представления алгоритмов в виде объектов.
Так же задача может включать перечень условий, при выполнении которых
имеет смысл применять данный паттерн.
Решение представляет собой описание элементов дизайна, отношений между
ними, функций каждого элемента. Конкретный дизайн или реализация не
имеются ввиду, поскольку паттерн – это шаблон, применимый в самых разных
ситуациях.
Результаты - это следствия применения паттерна и разного рода компромиссы.
Хотя при описании проектных решений о последствиях часто не упоминают,
знать о них необходимо, чтобы можно было выбрать между различными
вариантами и оценить преимущества и недостатки данного паттерна.
http://www.slideshare.net/IgorShkulipa 21
Классификация паттернов
Паттерны проектирования программных систем делятся на
следующие категории:
Архитектурные паттерны. Описывают структурную схему
программной системы в целом. В данной схеме указываются
отдельные функциональные составляющие системы,
называемые подсистемами, а также взаимоотношения между
ними.
Паттерны проектирования. описывают схемы детализации
программных подсистем и отношений между ними, при этом
они не влияют на структуру программной системы в целом и
сохраняют независимость от реализации языка
программирования.
Идиомы - низкоуровневые паттерны, имеют дело с вопросами
реализации какой-либо проблемы с учетом особенностей
данного языка программирования.
http://www.slideshare.net/IgorShkulipa 22
Паттерны проектирования
Паттерны проектирования делятся на следующие категории:
Порождающие - шаблоны проектирования, которые
абстрагируют процесс создания объектов. Они позволяют
сделать систему независимой от способа создания, композиции
и представления объектов.
Структурные - шаблоны проектирования, в которых
рассматривается вопрос о том, как из классов и объектов
образуются более крупные структуры.
Поведенческие - шаблоны проектирования, определяющие
алгоритмы и способы реализации взаимодействия различных
объектов и классов.
http://www.slideshare.net/IgorShkulipa 23
Порождающие паттерны
● Singleton (Одиночка) - контролирует создание единственного
экземпляра некоторого класса и предоставляет доступ к нему.
● Factory Method (Фабричный метод) - В его классическом варианте
вводится полиморфный класс Factory, в котором определяется
интерфейс фабричного метода, а ответственность за создание
объектов конкретных классов переносится на производные от Factory
классы, в которых этот метод переопределяется.
● Abstract Factory (Абстрактная фабрика) - использует несколько
фабричных методов и предназначен для создания целого семейства
или группы взаимосвязанных объектов.
● Builder (Строитель) - определяет процесс поэтапного
конструирования сложного объекта, в результате которого могут
получаться разные представления этого объекта.
● Prototype (Прототип) - создает новые объекты с помощью
прототипов. Прототип - некоторый объект, умеющий создавать по
запросу копию самого себя.
● Object Pool (Пул объектов) - используется в случае, когда создание
объекта требует больших затрат или может быть создано только
ограниченное количество объектов некоторого класса.
http://www.slideshare.net/IgorShkulipa 24
Singleton
Архитектура паттерна Singleton основана на идее использования
глобальной переменной, имеющей следующие свойства:
◦ Такая переменная доступна всегда. Время жизни глобальной
переменной - от запуска программы до ее завершения.
◦ Предоставляет глобальный доступ, то есть, такая
переменная может быть доступна из любой части
программы.
Singleton возлагает контроль над созданием единственного
объекта на сам класс.
Доступ к этому объекту осуществляется через статический метод
класса, который возвращает указатель или ссылку на него.
Этот объект будет создан только при первом обращении к
методу, а все последующие вызовы просто возвращают его
адрес.
Для обеспечения уникальности объекта, конструкторы и
оператор присваивания объявляются закрытыми.
http://www.slideshare.net/IgorShkulipa 25
Реализация Singleton
class Singleton
{
public:
static Singleton* GetInstance()
{
_instance=new Singleton();
return _instance;
}
void Method1(){cout<<"Method1n";}
void Method2(){cout<<"Method2n";}
private:
Singleton(){}
static Singleton* _instance;
};
Singleton::_instance=NULL;
int main()
{
Singleton* singleton = Singleton::GetInstance();
singleton->Method1();
singleton->Method2();
}
http://www.slideshare.net/IgorShkulipa 26
Преимущества и недостатки
Преимущества паттерна Singleton
◦ Класс сам контролирует процесс создания единственного
экземпляра.
◦ Паттерн легко адаптировать для создания нужного числа
экземпляров.
◦ Возможность создания объектов классов, производных от
Singleton.
Недостатки паттерна Singleton
◦ В случае использования нескольких взаимозависимых
одиночек их реализация может усложниться.
http://www.slideshare.net/IgorShkulipa 27
Factory Method
Паттерн Factory Method может быть полезным в решении
следующих задач:
1. Система должна оставаться расширяемой путем добавления
объектов новых типов. Непосредственное использование
оператора new является нежелательным, так как в этом
случае код создания объектов с указанием конкретных типов
может получиться разбросанным по всему приложению. Тогда
такие операции как добавление в систему объектов новых
типов или замена объектов одного типа на другой будут
затруднительными. Паттерн Factory Method позволяет системе
оставаться независимой как от самого процесса порождения
объектов, так и от их типов.
1. Заранее известно, когда нужно создавать объект, но
неизвестен его тип.
http://www.slideshare.net/IgorShkulipa 28
Описание паттерна Factory Method
Для того, чтобы система оставалась независимой от различных типов
объектов, паттерн Factory Method использует механизм полиморфизма -
классы всех конечных типов наследуются от одного абстрактного
базового класса, предназначенного для полиморфного использования. В
этом базовом классе определяется единый интерфейс, через который
пользователь будет оперировать объектами конечных типов.
Для обеспечения относительно простого добавления в систему новых типов
паттерн Factory Method локализует создание объектов конкретных типов в
специальном классе-фабрике. Методы этого класса, посредством которых
создаются объекты конкретных классов, называются фабричными.
Существуют две разновидности паттерна Factory Method:
Обобщенный конструктор, когда в том же самом полиморфном базовом
классе, от которого наследуются производные классы всех создаваемых в
системе типов, определяется статический фабричный метод. В качестве
параметра в этот метод должен передаваться идентификатор типа
создаваемого объекта.
Классический вариант фабричного метода, когда интерфейс фабричных
методов объявляется в независимом классе-фабрике, а их реализация
определяется конкретными подклассами этого класса.
http://www.slideshare.net/IgorShkulipa 29
Классическая реализация Factory Method. Классы-
результаты фабрики
class BaseClass {
public:
virtual string GetName() = 0;
virtual ~BaseClass(){}
};
class DerivedClass1 : public BaseClass {
public:
virtual string GetName() {
return "Derived Class 1n";
}
};
class DerivedClass2 : public BaseClass {
public:
virtual string GetName() {
return "Derived Class 2n";
}
};
http://www.slideshare.net/IgorShkulipa 30
Классическая реализация Factory Method. Классы
фабрик
class Factory {
public:
virtual BaseClass* FactoryMethod() = 0;
};
class Factory1 : public Factory {
public:
virtual BaseClass* FactoryMethod() {
return new DerivedClass1();
}
};
class Factory2 : public Factory {
public:
virtual BaseClass* FactoryMethod() {
return new DerivedClass2();
}
};
http://www.slideshare.net/IgorShkulipa 31
Классическая реализация Factory Method. Функция
main
int main()
{
const int count = 2;
Factory1 fact1;
Factory2 fact2;
Factory* factories[] = {&fact1, &fact2};
for (int i = 0; i < count; i++)
{
BaseClass* bc = factories[i]->FactoryMethod();
cout << bc->GetName().c_str();
delete bc;
}
} Результат:
Derived Class 1
Derived Class 2
http://www.slideshare.net/IgorShkulipa 32
Преимущества и недостатки
Преимущества паттерна Factory Method:
◦ Создает объекты разных типов, позволяя системе оставаться
независимой как от самого процесса создания, так и от
типов создаваемых объектов.
Недостатки паттерна Factory Method:
◦ В случае классического варианта паттерна даже для
порождения единственного объекта необходимо создавать
соответствующую фабрику
http://www.slideshare.net/IgorShkulipa 33
Abstract Factory
Паттерн Abstract Factory стоит использовать, когда:
◦ Система должна оставаться независимой как от процесса
создания новых объектов, так и от типов порождаемых
объектов. Непосредственное использование оператора new в
коде приложения нежелательно.
◦ Необходимо создавать группы или семейства
взаимосвязанных объектов, исключая возможность
одновременного использования объектов из разных
семейств в одном контексте.
http://www.slideshare.net/IgorShkulipa 34
Классы для 1-й фабрики
class BaseClass1 {
public:
virtual string GetName() = 0;
virtual ~BaseClass1(){}
};
class DerivedClass11: public BaseClass1 {
public:
virtual string GetName() {
return "Derived Class 11n";
}
};
class DerivedClass21 : public BaseClass1 {
public:
virtual string GetName() {
return "Derived Class 21n";
}
};
http://www.slideshare.net/IgorShkulipa 35
Классы для 2-й фабрики
class BaseClass2 {
public:
virtual string GetName() = 0;
virtual ~BaseClass2(){}
};
class DerivedClass12: public BaseClass2 {
public:
virtual string GetName() {
return "Derived Class 12n";
}
};
class DerivedClass22 : public BaseClass2 {
public:
virtual string GetName() {
return "Derived Class 22n";
}
};
http://www.slideshare.net/IgorShkulipa 36
Классы фабрик
class AbstractFactory {
public:
virtual BaseClass1* FactoryMethod1() = 0;
virtual BaseClass2* FactoryMethod2() = 0;
};
class AbstractFactory1 : public AbstractFactory {
public:
virtual BaseClass1* FactoryMethod1() {
return new DerivedClass11(); }
virtual BaseClass2* FactoryMethod2() {
return new DerivedClass12(); }
};
class AbstractFactory2 : public AbstractFactory {
public:
virtual BaseClass1* FactoryMethod1() {
return new DerivedClass21(); }
virtual BaseClass2* FactoryMethod2() {
return new DerivedClass22(); }
};
http://www.slideshare.net/IgorShkulipa 37
Функция main
void UseAbstractFactory(AbstractFactory* af)
{
BaseClass1* bc1=af->FactoryMethod1();
BaseClass2* bc2=af->FactoryMethod2();
cout<<bc1->GetName().c_str();
cout<<bc2->GetName().c_str();
}
int main()
{
AbstractFactory* af1=new AbstractFactory1();
AbstractFactory* af2=new AbstractFactory2();
UseAbstractFactory(af1);
UseAbstractFactory(af2);
}
Результат:
Derived Class 11
Derived Class 12
Derived Class 21
Derived Class 22
http://www.slideshare.net/IgorShkulipa 38
Builder
Паттерн Builder может помочь в решении следующих задач:
◦ В системе могут существовать сложные объекты, создание
которых за одну операцию затруднительно или невозможно.
Требуется поэтапное построение объектов с контролем
результатов выполнения каждого этапа.
◦ Данные должны иметь несколько представлений. Приведем
классический пример. Пусть есть некоторый исходный
документ в формате RTF (Rich Text Format), в общем случае
содержащий текст, графические изображения и служебную
информацию о форматировании (размер и тип шрифтов,
отступы и др.). Если этот документ в формате RTF
преобразовать в другие форматы (например, Microsoft Word
или простой ASCII-текст), то полученные документы и будут
представлениями исходных данных.
http://www.slideshare.net/IgorShkulipa 39
Описание паттерна Builder
Паттерн Builder отделяет алгоритм поэтапного конструирования
сложного объекта от его внешнего представления так, что с
помощью одного и того же алгоритма можно получать разные
представления этого объекта.
Для этого паттерн Builder определяет алгоритм поэтапного
создания продукта в специальном классе Director
(распорядитель), а ответственность за координацию процесса
сборки отдельных частей продукта возлагает на иерархию
классов Builder. В этой иерархии базовый класс Builder
объявляет интерфейс для построения отдельных частей
продукта, а соответствующие подклассы конкретных
строителей их реализуют подходящим образом, например,
создают или получают нужные ресурсы, сохраняют
промежуточные результаты, контролируют результаты
выполнения операций.
http://www.slideshare.net/IgorShkulipa 40
Реализация паттерна Builder. Класс Computer
class Computer {
// Поля сделаны паблик, в виду
// ограниченности места на слайде.
// В реальности, это надо реализовывать
// через методы доступа к приватным полям!
public:
string Name;
string CPU;
string RAM;
string HDD;
string VGA;
void Print() {
cout<<Name.c_str()<<": "
<<CPU.c_str()<<“ / "
<<RAM.c_str()<<“ / "
<<HDD.c_str()<<“ / "
<<VGA.c_str()<<"n";
}
};
http://www.slideshare.net/IgorShkulipa 41
Классы-строители
class ICompBuilder {
public: virtual Computer Build()=0;
};
class GameCompBuilder: public ICompBuilder {
private: Computer _computer;
public: GameCompBuilder(){};
virtual Computer Build() {
_computer.Name="Game Computer";
_computer.CPU="Core i7 3.0 GHz";
_computer.RAM="8 Gb";
_computer.HDD="1 Tb";
_computer.VGA="GeForce GTX 560";
return _computer;
} };
class OfficeCompBuilder: public ICompBuilder {
private: Computer _computer;
public: OfficeCompBuilder(){};
virtual Computer Build() {
_computer.Name="Office Computer";
_computer.CPU="Core i3 3.0 GHz";
_computer.RAM="2 Gb";
_computer.HDD="500 Gb";
_computer.VGA="Intel GMA 4000";
return _computer;
} };
http://www.slideshare.net/IgorShkulipa 42
Класс-директор
class Director {
public:
Director(){_builder=NULL;};
void SetBuilder(ICompBuilder* builder)
{
_builder=builder;
}
Computer GetComp()
{
return _builder->Build();
}
private:
ICompBuilder* _builder;
};
http://www.slideshare.net/IgorShkulipa 43
Использование строителей
int main()
{
Director* director=new Director();
//Building Game Computer
director->SetBuilder(new GameCompBuilder);
Computer comp=director->GetComp();
comp.Print();
//Building Office Computer
director->SetBuilder(new OfficeCompBuilder);
comp=director->GetComp();
comp.Print();
}
Результат:
Game Computer: Core i7 3.0 GHz / 8 Gb / 1 Tb / GeForce GTX 560
Office Computer: Core i3 3.0 GHz / 2 Gb / 500 Gb / Intel GMA 4000
http://www.slideshare.net/IgorShkulipa 44
Преимущества и недостатки
Преимущества паттерна Builder:
◦ Возможность контролировать процесс создания сложного
продукта.
◦ Возможность получения разных представлений некоторых
данных.
Недостатки паттерна Builder:
◦ Конкретный строитель и создаваемый им продукт жестко
связаны между собой, поэтому при внесении изменений в
класс продукта придется соответствующим образом изменять
и класс конкретного строителя.
http://www.slideshare.net/IgorShkulipa 45
Prototype
Паттерн Prototype (прототип) можно использовать в следующих
случаях:
◦ Система должна оставаться независимой как от процесса
создания новых объектов, так и от типов порождаемых
объектов. Непосредственное использование оператора new в
коде приложения считается нежелательным.
◦ Необходимо создавать объекты, точные классы которых
становятся известными уже на стадии выполнения
программы.
http://www.slideshare.net/IgorShkulipa 46
Реализация Prototype
class IProduct {
public:
virtual IProduct* Clone()=0;
virtual void Print()=0;
};
class Product1: public IProduct {
private:
string _strSomeField1;
public:
Product1(string strField) {
_strSomeField1=strField;
}
virtual IProduct* Clone() {
return new Product1(*this);
}
virtual void Print() {
cout<<"Address: "<<this<<"n";
cout<<"Field: "<<_strSomeField1.c_str()<<"n";
}
};
http://www.slideshare.net/IgorShkulipa 47
Реализация Prototype
class Product2: public IProduct {
private:
string _strSomeField2;
public:
Product2()
{
_strSomeField2="";
}
virtual void Print() {
cout<<"Address: "<<this<<"n";
cout<<"Field: "<<_strSomeField2.c_str()<<"n";
}
};
http://www.slideshare.net/IgorShkulipa 48
Использование Prototype
int main()
{
IProduct* prod1=new Product1("Product 1");
IProduct* prod2=prod1->Clone();
Product1* prod3=(Product1*)prod1->Clone();
Product2* prod4=(Product2*)prod1->Clone();
prod1->Print();
prod2->Print();
prod3->Print();
prod4->Print();
}
Результат:
Address: 004492E8
Field: Product 1
Address: 00449348
Field: Product 1
Address: 004493A8
Field: Product 1
Address: 00449408
Field: Product 1
http://www.slideshare.net/IgorShkulipa 49
Преимущества и недостатки
Преимущества паттерна Prototype
◦ Для создания новых объектов клиенту необязательно знать
их конкретные классы.
◦ Возможность гибкого управления процессом создания новых
объектов за счет возможности динамического добавления и
удаления прототипов.
Недостатки паттерна Prototype
◦ Каждый тип создаваемого продукта должен реализовывать
операцию клонирования clone(). В случае, если требуется
глубокое копирование объекта (объект содержит ссылки или
указатели на другие объекты), это может быть непростой
задачей.
http://www.slideshare.net/IgorShkulipa 50
Object Pool
Применение паттерна Object Pool может значительно повысить
производительность системы; его использование наиболее
эффективно в ситуациях, когда создание экземпляров
некоторого класса требует больших затрат, объекты в системе
создаются часто, но число создаваемых объектов в единицу
времени ограничено.
Пулы объектов (известны также как пулы ресурсов)
используются для управления кэшированием объектов.
Клиент, имеющий доступ к пулу объектов может избежать
создания новых объектов, просто запрашивая в пуле уже
созданный экземпляр. Пул объектов может быть растущим,
когда при отсутствии свободных создаются новые объекты или
c ограничением количества создаваемых объектов.
http://www.slideshare.net/IgorShkulipa 51
Реализация Object Pool на основе Singleton. Классы
объектов
class IObject {
protected:
string _strText;
public:
virtual void Print() {
cout<<"The Object is: "<<_strText.c_str()<<"n";
} };
class Object1: public IObject {
public:
Object1(){
_strText="Object 1";
} };
class Object2: public IObject {
public:
Object2(){
_strText="Object 2";
} };
class Object3: public IObject {
public:
Object3(){
_strText="Object 3";
} };
class Object4: public IObject {
public:
Object4(){
_strText="Object 4";
} };
http://www.slideshare.net/IgorShkulipa 52
Object Pool
template<unsigned poolSize> class ObjectPool{
public:
static ObjectPool* GetInstance() {
if (!_instance) _instance=new ObjectPool();
return _instance;
}
IObject* GetObject() {
for (unsigned i=0;i<poolSize;i++)
{ if (!_busyObjects[i]){
_busyObjects[i]=true;
return _objectPool[i];
} }
return NULL;
}
void ReleaseObject(IObject* object){
for (unsigned i=0;i<poolSize;i++)
{ if (_objectPool[i]==object){
_busyObjects[i]=false;
} }
}
...
http://www.slideshare.net/IgorShkulipa 53
Object Pool
...
private:
ObjectPool(){
for (unsigned i=0;i<poolSize;i++)
{
unsigned iObjNumber=rand()%4;
switch (iObjNumber)
{
case 0: _objectPool[i]=new Object1(); break;
case 1: _objectPool[i]=new Object2(); break;
case 2: _objectPool[i]=new Object3(); break;
case 3: _objectPool[i]=new Object4(); break;
}
_busyObjects[i]=false;
}
}
private:
IObject* _objectPool[poolSize];
bool _busyObjects[poolSize];
static ObjectPool* _instance;
};
http://www.slideshare.net/IgorShkulipa 54
Использование Object Pool
template<unsigned poolSize>
ObjectPool<poolSize>* ObjectPool<poolSize>::_instance=NULL;
int main()
{
ObjectPool<5>* op=ObjectPool<5>::GetInstance();
IObject* object1=op->GetObject();
if (object1) object1->Print(); else cout<<"The Object is: NULLn";
IObject* object2=op->GetObject();
if (object2) object2->Print(); else cout<<"The Object is: NULLn";
IObject* object3=op->GetObject();
if (object3) object3->Print(); else cout<<"The Object is: NULLn";
IObject* object4=op->GetObject();
if (object4) object4->Print(); else cout<<"The Object is: NULLn";
IObject* object5=op->GetObject();
if (object5) object5->Print(); else cout<<"The Object is: NULLn";
IObject* object6=op->GetObject();
if (object6) object6->Print(); else cout<<"The Object is: NULLn";
IObject* object7=op->GetObject();
if (object7) object7->Print(); else cout<<"The Object is: NULLn";
op->ReleaseObject(object2);
IObject* object8=op->GetObject();
if (object8) object8->Print(); else cout<<"The Object is: NULLn";
}
http://www.slideshare.net/IgorShkulipa 55
Результат
The Object is: Object 2
The Object is: Object 4
The Object is: Object 3
The Object is: Object 1
The Object is: Object 2
The Object is: NULL
The Object is: NULL
The Object is: Object 4
http://www.slideshare.net/IgorShkulipa 56
Преимущества и недостатки
◦ Пул объектов отслеживает объекты, которые он создает.
◦ Паттерн Object Pool может использоваться для инкапсуляции
логики создания объектов. Однако он не управляет ими
после их создания.
◦ Достоинством этого паттерна является быстрое создание
объектов, однако это реализовано за счет использования
больших ресурсов памяти.
http://www.slideshare.net/IgorShkulipa 57
Лабораторная работа №16. Порождающие шаблоны
1. Создать класс «Игральная карта» и наследников от него.
Создать класс «Колода карт», обрабатывающий выдачу карт
игроку на основе паттерна Object Pool.
1. Создать классы «Односвязный список», «Двусвязный список»,
«Циклический список», на основе общего интерфейса.
Реализовать методы доступа к элементам, сортировки и
нахождения максимума/минимума. Реализовать создание
конкретных списков на основе фабричного метода.

C++ Базовый. Занятие 16.

  • 1.
    Модуль 4: Дополнительныетемы объектно-ориентированного программирования. Темы лекции: Отношения между классами. Порождающие шаблоны проектирования. Практическое задание: Порождающие шаблоны. Тренер: Игорь Шкулипа, к.т.н. C++ Базовый. Занятие 16
  • 2.
  • 3.
    http://www.slideshare.net/IgorShkulipa 3 Ассоциация Ассоциация -самый слабый вид связи. Обычно ассоциация возникает, когда один класс вызывает метод другого или если при вызове метода в качестве аргумента передаётся объект другого класса.
  • 4.
    http://www.slideshare.net/IgorShkulipa 4 Агрегация Агрегация (присоединение,включение) - агрегирование или соединение частей. Например, когда класс содержит поле – объект другого класса.
  • 5.
    http://www.slideshare.net/IgorShkulipa 5 Композиция Композиция классов- более сильная связь между классами, чем агрегация. Между агрегацией и композицией довольно тонкая грань. Особенностью композиции является то, что объекты, из которых создаётся композиция, могут принадлежать только классу, с которым они образуют композицию. При этом время жизни объекта и класса, в который встраивается объект, совпадает.
  • 6.
    http://www.slideshare.net/IgorShkulipa 6 Наследование Наследование –это отношение между классами, при котором класс-наследник использует поля и методы родительского класса. Класс, структура и поведение которого наследуется, называется суперклассом, надклассом, базовым или родительским классом Класс, производный от суперкласса, называется подклассом, производным или дочерним классом
  • 7.
    http://www.slideshare.net/IgorShkulipa 7 Формы наследования Вподклассе структура и поведение исходного суперкласса могут дополняться, переопределяться или ограничиваться. Исходя из этого, можно выделить различные формы наследования: 1. Специализация 2. Спецификация 3. Обобщение 4. Расширение 5. Ограничение 6. Конструирование 7. Варьирование 8. Комбинирование
  • 8.
    http://www.slideshare.net/IgorShkulipa 8 Специализация Дочерний классявляется более конкретным, частным или специализированным случаем родительского класса. Дочерний класс удовлетворяет спецификациям родителя во всех существенных моментах, т.е. его можно использовать вместо родительского класса. Поведение базового класса, в основном, переопределяется. специализация
  • 9.
    http://www.slideshare.net/IgorShkulipa 9 Спецификация Родительский классописывает поведение, которое реализуется в дочернем классе, но оставлено нереализованным в родительском В таких случаях родительский класс называют абстрактно- специфицированным классом. Пример: интерфейсы. спецификацияспецификацияспецификация
  • 10.
    http://www.slideshare.net/IgorShkulipa 10 Обобщение Дочерний классмодифицирует и переопределяет некоторые методы родительского класса с целью получения объекта более общей категории. Данная форма наследования противоположна специализации и обычно применяется, когда построение происходит на основе существующих классов, которые мы не хотим или не можем изменять. Пример: Массив и контейнер. обобщение
  • 11.
    http://www.slideshare.net/IgorShkulipa 11 Расширение Дочерний классдобавляет новые функциональные возможности к родительскому классу, но не меняет наследуемое поведение. В отличие от обобщения или специализации при расширении дочерний класс не переопределяет ни одного метода базового класса, а добавленные методы слабо связаны с существующими методами родителя. расширение
  • 12.
    http://www.slideshare.net/IgorShkulipa 12 Ограничение Дочерний классограничивает использование некоторых методов родительского класса. Так же, как и при обобщении, применение для ограничения чаще всего возникает, когда, класс строится на основе существующей иерархии классов, которая не должна или не может быть изменена. Пример: Список, стек, очередь ограничениеограничение
  • 13.
    http://www.slideshare.net/IgorShkulipa 13 Конструирование Дочерний классиспользует методы, поставляемые родительским классом. Между дочерним и родительским классами отсутствует отношение «is-a» или «быть экземпляром», то есть дочерний класс не является более специализированной формой родительского класса. Обычно для реализации такой формы наследования используется механизм закрытого наследования. Дочерний класс часто изменяет не только имена методов базового класса, но и аргументы. конструирование
  • 14.
    http://www.slideshare.net/IgorShkulipa 14 Варьирование Варьирование наблюдается,когда два класса имеют сходную реализацию и находятся примерно на одном уровне иерархии, то есть являются частными случаями более общего понятия. варьирование
  • 15.
    http://www.slideshare.net/IgorShkulipa 15 Комбинирование Дочерний класснаследует черты более чем одного родительского класса. Для комбинирования классов используется механизм множественного наследования.
  • 16.
    http://www.slideshare.net/IgorShkulipa 16 Правильные формынаследования (is-a) К правильным формам относятся только те, в которых между дочерним и родительским классом возникает отношение "is- a" или «быть экземпляром», то есть дочерний класс является частным случаем своего предка. Если B не есть A, то B не стоит наследовать от A. Пример: Прямоугольник не является частным случаем квадрата, по этому, класс Rectangle не стоит наследовать от класса Square. Правильные формы наследования is-a: ⚫ Специализация ⚫ Спецификация ⚫ Расширение (в большинстве случаев) ⚫ Ограничение (иногда) ⚫ Комбинирование (иногда)
  • 17.
    http://www.slideshare.net/IgorShkulipa 17 Правильные формынаследования (тип-подтип) Для того чтобы класс B был подтипом класса A, он должен иметь схожее поведение с классом A во всех существенных моментах. Схожее поведение означает, что класс B должен поддерживать тот же набор методов, что и класс A. Пример: Базовый: Геометрическая фигура. Наследники: Окружность, Треугольник, Прямоугольник и т.д. Правильные формы наследования "тип−подтип“: ⚫ Специализация ⚫ Спецификация ⚫ Обобщение ⚫ Расширение ⚫ Конструирование (очень редко) ⚫ Варьирование (иногда) ⚫ Комбинирование
  • 18.
    http://www.slideshare.net/IgorShkulipa 18 Формы наследования Абсолютноправильные формы наследования: ⚫ Специализация ⚫ Спецификация ⚫ Расширение - если производный класс есть частный случай базового ⚫ Комбинирование- если производный класс есть частный случай базовых и является подтипом базовых типов Допустимые формы наследования: ⚫ Обобщение ⚫ Ограничение ⚫ Варьирование ⚫ Комбинирование - если производный класс не является частным случаем базовых Недопустимые формы наследования: ⚫ Конструирование всегда является недопустимой формой наследования и всегда может быть заменено другими формами наследования и отношениями между классами.
  • 19.
  • 20.
    http://www.slideshare.net/IgorShkulipa 20 Паттерны (шаблоныпроектирования) Паттерн описывает задачу, которая снова и снова возникает в работе, а так же принцип ее решения, причем таким образом, что это решение можно потом использовать много раз, ничего не изобретая заново. В общем случае паттерн состоит из четырех основных элементов: Имя. Присваивание паттернам имен позволяет проектировать на более высоком уровне абстракции. С помощью имен паттернов можно вести общение с коллегами. Назначение паттернам имен упрощает общение в профессиональной среде. Задача - это описание того, когда следует применять паттерн. Необходимо сформулировать задачу и ее контекст. Может описываться конкретная проблема проектирования, например способ представления алгоритмов в виде объектов. Так же задача может включать перечень условий, при выполнении которых имеет смысл применять данный паттерн. Решение представляет собой описание элементов дизайна, отношений между ними, функций каждого элемента. Конкретный дизайн или реализация не имеются ввиду, поскольку паттерн – это шаблон, применимый в самых разных ситуациях. Результаты - это следствия применения паттерна и разного рода компромиссы. Хотя при описании проектных решений о последствиях часто не упоминают, знать о них необходимо, чтобы можно было выбрать между различными вариантами и оценить преимущества и недостатки данного паттерна.
  • 21.
    http://www.slideshare.net/IgorShkulipa 21 Классификация паттернов Паттерныпроектирования программных систем делятся на следующие категории: Архитектурные паттерны. Описывают структурную схему программной системы в целом. В данной схеме указываются отдельные функциональные составляющие системы, называемые подсистемами, а также взаимоотношения между ними. Паттерны проектирования. описывают схемы детализации программных подсистем и отношений между ними, при этом они не влияют на структуру программной системы в целом и сохраняют независимость от реализации языка программирования. Идиомы - низкоуровневые паттерны, имеют дело с вопросами реализации какой-либо проблемы с учетом особенностей данного языка программирования.
  • 22.
    http://www.slideshare.net/IgorShkulipa 22 Паттерны проектирования Паттерныпроектирования делятся на следующие категории: Порождающие - шаблоны проектирования, которые абстрагируют процесс создания объектов. Они позволяют сделать систему независимой от способа создания, композиции и представления объектов. Структурные - шаблоны проектирования, в которых рассматривается вопрос о том, как из классов и объектов образуются более крупные структуры. Поведенческие - шаблоны проектирования, определяющие алгоритмы и способы реализации взаимодействия различных объектов и классов.
  • 23.
    http://www.slideshare.net/IgorShkulipa 23 Порождающие паттерны ●Singleton (Одиночка) - контролирует создание единственного экземпляра некоторого класса и предоставляет доступ к нему. ● Factory Method (Фабричный метод) - В его классическом варианте вводится полиморфный класс Factory, в котором определяется интерфейс фабричного метода, а ответственность за создание объектов конкретных классов переносится на производные от Factory классы, в которых этот метод переопределяется. ● Abstract Factory (Абстрактная фабрика) - использует несколько фабричных методов и предназначен для создания целого семейства или группы взаимосвязанных объектов. ● Builder (Строитель) - определяет процесс поэтапного конструирования сложного объекта, в результате которого могут получаться разные представления этого объекта. ● Prototype (Прототип) - создает новые объекты с помощью прототипов. Прототип - некоторый объект, умеющий создавать по запросу копию самого себя. ● Object Pool (Пул объектов) - используется в случае, когда создание объекта требует больших затрат или может быть создано только ограниченное количество объектов некоторого класса.
  • 24.
    http://www.slideshare.net/IgorShkulipa 24 Singleton Архитектура паттернаSingleton основана на идее использования глобальной переменной, имеющей следующие свойства: ◦ Такая переменная доступна всегда. Время жизни глобальной переменной - от запуска программы до ее завершения. ◦ Предоставляет глобальный доступ, то есть, такая переменная может быть доступна из любой части программы. Singleton возлагает контроль над созданием единственного объекта на сам класс. Доступ к этому объекту осуществляется через статический метод класса, который возвращает указатель или ссылку на него. Этот объект будет создан только при первом обращении к методу, а все последующие вызовы просто возвращают его адрес. Для обеспечения уникальности объекта, конструкторы и оператор присваивания объявляются закрытыми.
  • 25.
    http://www.slideshare.net/IgorShkulipa 25 Реализация Singleton classSingleton { public: static Singleton* GetInstance() { _instance=new Singleton(); return _instance; } void Method1(){cout<<"Method1n";} void Method2(){cout<<"Method2n";} private: Singleton(){} static Singleton* _instance; }; Singleton::_instance=NULL; int main() { Singleton* singleton = Singleton::GetInstance(); singleton->Method1(); singleton->Method2(); }
  • 26.
    http://www.slideshare.net/IgorShkulipa 26 Преимущества инедостатки Преимущества паттерна Singleton ◦ Класс сам контролирует процесс создания единственного экземпляра. ◦ Паттерн легко адаптировать для создания нужного числа экземпляров. ◦ Возможность создания объектов классов, производных от Singleton. Недостатки паттерна Singleton ◦ В случае использования нескольких взаимозависимых одиночек их реализация может усложниться.
  • 27.
    http://www.slideshare.net/IgorShkulipa 27 Factory Method ПаттернFactory Method может быть полезным в решении следующих задач: 1. Система должна оставаться расширяемой путем добавления объектов новых типов. Непосредственное использование оператора new является нежелательным, так как в этом случае код создания объектов с указанием конкретных типов может получиться разбросанным по всему приложению. Тогда такие операции как добавление в систему объектов новых типов или замена объектов одного типа на другой будут затруднительными. Паттерн Factory Method позволяет системе оставаться независимой как от самого процесса порождения объектов, так и от их типов. 1. Заранее известно, когда нужно создавать объект, но неизвестен его тип.
  • 28.
    http://www.slideshare.net/IgorShkulipa 28 Описание паттернаFactory Method Для того, чтобы система оставалась независимой от различных типов объектов, паттерн Factory Method использует механизм полиморфизма - классы всех конечных типов наследуются от одного абстрактного базового класса, предназначенного для полиморфного использования. В этом базовом классе определяется единый интерфейс, через который пользователь будет оперировать объектами конечных типов. Для обеспечения относительно простого добавления в систему новых типов паттерн Factory Method локализует создание объектов конкретных типов в специальном классе-фабрике. Методы этого класса, посредством которых создаются объекты конкретных классов, называются фабричными. Существуют две разновидности паттерна Factory Method: Обобщенный конструктор, когда в том же самом полиморфном базовом классе, от которого наследуются производные классы всех создаваемых в системе типов, определяется статический фабричный метод. В качестве параметра в этот метод должен передаваться идентификатор типа создаваемого объекта. Классический вариант фабричного метода, когда интерфейс фабричных методов объявляется в независимом классе-фабрике, а их реализация определяется конкретными подклассами этого класса.
  • 29.
    http://www.slideshare.net/IgorShkulipa 29 Классическая реализацияFactory Method. Классы- результаты фабрики class BaseClass { public: virtual string GetName() = 0; virtual ~BaseClass(){} }; class DerivedClass1 : public BaseClass { public: virtual string GetName() { return "Derived Class 1n"; } }; class DerivedClass2 : public BaseClass { public: virtual string GetName() { return "Derived Class 2n"; } };
  • 30.
    http://www.slideshare.net/IgorShkulipa 30 Классическая реализацияFactory Method. Классы фабрик class Factory { public: virtual BaseClass* FactoryMethod() = 0; }; class Factory1 : public Factory { public: virtual BaseClass* FactoryMethod() { return new DerivedClass1(); } }; class Factory2 : public Factory { public: virtual BaseClass* FactoryMethod() { return new DerivedClass2(); } };
  • 31.
    http://www.slideshare.net/IgorShkulipa 31 Классическая реализацияFactory Method. Функция main int main() { const int count = 2; Factory1 fact1; Factory2 fact2; Factory* factories[] = {&fact1, &fact2}; for (int i = 0; i < count; i++) { BaseClass* bc = factories[i]->FactoryMethod(); cout << bc->GetName().c_str(); delete bc; } } Результат: Derived Class 1 Derived Class 2
  • 32.
    http://www.slideshare.net/IgorShkulipa 32 Преимущества инедостатки Преимущества паттерна Factory Method: ◦ Создает объекты разных типов, позволяя системе оставаться независимой как от самого процесса создания, так и от типов создаваемых объектов. Недостатки паттерна Factory Method: ◦ В случае классического варианта паттерна даже для порождения единственного объекта необходимо создавать соответствующую фабрику
  • 33.
    http://www.slideshare.net/IgorShkulipa 33 Abstract Factory ПаттернAbstract Factory стоит использовать, когда: ◦ Система должна оставаться независимой как от процесса создания новых объектов, так и от типов порождаемых объектов. Непосредственное использование оператора new в коде приложения нежелательно. ◦ Необходимо создавать группы или семейства взаимосвязанных объектов, исключая возможность одновременного использования объектов из разных семейств в одном контексте.
  • 34.
    http://www.slideshare.net/IgorShkulipa 34 Классы для1-й фабрики class BaseClass1 { public: virtual string GetName() = 0; virtual ~BaseClass1(){} }; class DerivedClass11: public BaseClass1 { public: virtual string GetName() { return "Derived Class 11n"; } }; class DerivedClass21 : public BaseClass1 { public: virtual string GetName() { return "Derived Class 21n"; } };
  • 35.
    http://www.slideshare.net/IgorShkulipa 35 Классы для2-й фабрики class BaseClass2 { public: virtual string GetName() = 0; virtual ~BaseClass2(){} }; class DerivedClass12: public BaseClass2 { public: virtual string GetName() { return "Derived Class 12n"; } }; class DerivedClass22 : public BaseClass2 { public: virtual string GetName() { return "Derived Class 22n"; } };
  • 36.
    http://www.slideshare.net/IgorShkulipa 36 Классы фабрик classAbstractFactory { public: virtual BaseClass1* FactoryMethod1() = 0; virtual BaseClass2* FactoryMethod2() = 0; }; class AbstractFactory1 : public AbstractFactory { public: virtual BaseClass1* FactoryMethod1() { return new DerivedClass11(); } virtual BaseClass2* FactoryMethod2() { return new DerivedClass12(); } }; class AbstractFactory2 : public AbstractFactory { public: virtual BaseClass1* FactoryMethod1() { return new DerivedClass21(); } virtual BaseClass2* FactoryMethod2() { return new DerivedClass22(); } };
  • 37.
    http://www.slideshare.net/IgorShkulipa 37 Функция main voidUseAbstractFactory(AbstractFactory* af) { BaseClass1* bc1=af->FactoryMethod1(); BaseClass2* bc2=af->FactoryMethod2(); cout<<bc1->GetName().c_str(); cout<<bc2->GetName().c_str(); } int main() { AbstractFactory* af1=new AbstractFactory1(); AbstractFactory* af2=new AbstractFactory2(); UseAbstractFactory(af1); UseAbstractFactory(af2); } Результат: Derived Class 11 Derived Class 12 Derived Class 21 Derived Class 22
  • 38.
    http://www.slideshare.net/IgorShkulipa 38 Builder Паттерн Builderможет помочь в решении следующих задач: ◦ В системе могут существовать сложные объекты, создание которых за одну операцию затруднительно или невозможно. Требуется поэтапное построение объектов с контролем результатов выполнения каждого этапа. ◦ Данные должны иметь несколько представлений. Приведем классический пример. Пусть есть некоторый исходный документ в формате RTF (Rich Text Format), в общем случае содержащий текст, графические изображения и служебную информацию о форматировании (размер и тип шрифтов, отступы и др.). Если этот документ в формате RTF преобразовать в другие форматы (например, Microsoft Word или простой ASCII-текст), то полученные документы и будут представлениями исходных данных.
  • 39.
    http://www.slideshare.net/IgorShkulipa 39 Описание паттернаBuilder Паттерн Builder отделяет алгоритм поэтапного конструирования сложного объекта от его внешнего представления так, что с помощью одного и того же алгоритма можно получать разные представления этого объекта. Для этого паттерн Builder определяет алгоритм поэтапного создания продукта в специальном классе Director (распорядитель), а ответственность за координацию процесса сборки отдельных частей продукта возлагает на иерархию классов Builder. В этой иерархии базовый класс Builder объявляет интерфейс для построения отдельных частей продукта, а соответствующие подклассы конкретных строителей их реализуют подходящим образом, например, создают или получают нужные ресурсы, сохраняют промежуточные результаты, контролируют результаты выполнения операций.
  • 40.
    http://www.slideshare.net/IgorShkulipa 40 Реализация паттернаBuilder. Класс Computer class Computer { // Поля сделаны паблик, в виду // ограниченности места на слайде. // В реальности, это надо реализовывать // через методы доступа к приватным полям! public: string Name; string CPU; string RAM; string HDD; string VGA; void Print() { cout<<Name.c_str()<<": " <<CPU.c_str()<<“ / " <<RAM.c_str()<<“ / " <<HDD.c_str()<<“ / " <<VGA.c_str()<<"n"; } };
  • 41.
    http://www.slideshare.net/IgorShkulipa 41 Классы-строители class ICompBuilder{ public: virtual Computer Build()=0; }; class GameCompBuilder: public ICompBuilder { private: Computer _computer; public: GameCompBuilder(){}; virtual Computer Build() { _computer.Name="Game Computer"; _computer.CPU="Core i7 3.0 GHz"; _computer.RAM="8 Gb"; _computer.HDD="1 Tb"; _computer.VGA="GeForce GTX 560"; return _computer; } }; class OfficeCompBuilder: public ICompBuilder { private: Computer _computer; public: OfficeCompBuilder(){}; virtual Computer Build() { _computer.Name="Office Computer"; _computer.CPU="Core i3 3.0 GHz"; _computer.RAM="2 Gb"; _computer.HDD="500 Gb"; _computer.VGA="Intel GMA 4000"; return _computer; } };
  • 42.
    http://www.slideshare.net/IgorShkulipa 42 Класс-директор class Director{ public: Director(){_builder=NULL;}; void SetBuilder(ICompBuilder* builder) { _builder=builder; } Computer GetComp() { return _builder->Build(); } private: ICompBuilder* _builder; };
  • 43.
    http://www.slideshare.net/IgorShkulipa 43 Использование строителей intmain() { Director* director=new Director(); //Building Game Computer director->SetBuilder(new GameCompBuilder); Computer comp=director->GetComp(); comp.Print(); //Building Office Computer director->SetBuilder(new OfficeCompBuilder); comp=director->GetComp(); comp.Print(); } Результат: Game Computer: Core i7 3.0 GHz / 8 Gb / 1 Tb / GeForce GTX 560 Office Computer: Core i3 3.0 GHz / 2 Gb / 500 Gb / Intel GMA 4000
  • 44.
    http://www.slideshare.net/IgorShkulipa 44 Преимущества инедостатки Преимущества паттерна Builder: ◦ Возможность контролировать процесс создания сложного продукта. ◦ Возможность получения разных представлений некоторых данных. Недостатки паттерна Builder: ◦ Конкретный строитель и создаваемый им продукт жестко связаны между собой, поэтому при внесении изменений в класс продукта придется соответствующим образом изменять и класс конкретного строителя.
  • 45.
    http://www.slideshare.net/IgorShkulipa 45 Prototype Паттерн Prototype(прототип) можно использовать в следующих случаях: ◦ Система должна оставаться независимой как от процесса создания новых объектов, так и от типов порождаемых объектов. Непосредственное использование оператора new в коде приложения считается нежелательным. ◦ Необходимо создавать объекты, точные классы которых становятся известными уже на стадии выполнения программы.
  • 46.
    http://www.slideshare.net/IgorShkulipa 46 Реализация Prototype classIProduct { public: virtual IProduct* Clone()=0; virtual void Print()=0; }; class Product1: public IProduct { private: string _strSomeField1; public: Product1(string strField) { _strSomeField1=strField; } virtual IProduct* Clone() { return new Product1(*this); } virtual void Print() { cout<<"Address: "<<this<<"n"; cout<<"Field: "<<_strSomeField1.c_str()<<"n"; } };
  • 47.
    http://www.slideshare.net/IgorShkulipa 47 Реализация Prototype classProduct2: public IProduct { private: string _strSomeField2; public: Product2() { _strSomeField2=""; } virtual void Print() { cout<<"Address: "<<this<<"n"; cout<<"Field: "<<_strSomeField2.c_str()<<"n"; } };
  • 48.
    http://www.slideshare.net/IgorShkulipa 48 Использование Prototype intmain() { IProduct* prod1=new Product1("Product 1"); IProduct* prod2=prod1->Clone(); Product1* prod3=(Product1*)prod1->Clone(); Product2* prod4=(Product2*)prod1->Clone(); prod1->Print(); prod2->Print(); prod3->Print(); prod4->Print(); } Результат: Address: 004492E8 Field: Product 1 Address: 00449348 Field: Product 1 Address: 004493A8 Field: Product 1 Address: 00449408 Field: Product 1
  • 49.
    http://www.slideshare.net/IgorShkulipa 49 Преимущества инедостатки Преимущества паттерна Prototype ◦ Для создания новых объектов клиенту необязательно знать их конкретные классы. ◦ Возможность гибкого управления процессом создания новых объектов за счет возможности динамического добавления и удаления прототипов. Недостатки паттерна Prototype ◦ Каждый тип создаваемого продукта должен реализовывать операцию клонирования clone(). В случае, если требуется глубокое копирование объекта (объект содержит ссылки или указатели на другие объекты), это может быть непростой задачей.
  • 50.
    http://www.slideshare.net/IgorShkulipa 50 Object Pool Применениепаттерна Object Pool может значительно повысить производительность системы; его использование наиболее эффективно в ситуациях, когда создание экземпляров некоторого класса требует больших затрат, объекты в системе создаются часто, но число создаваемых объектов в единицу времени ограничено. Пулы объектов (известны также как пулы ресурсов) используются для управления кэшированием объектов. Клиент, имеющий доступ к пулу объектов может избежать создания новых объектов, просто запрашивая в пуле уже созданный экземпляр. Пул объектов может быть растущим, когда при отсутствии свободных создаются новые объекты или c ограничением количества создаваемых объектов.
  • 51.
    http://www.slideshare.net/IgorShkulipa 51 Реализация ObjectPool на основе Singleton. Классы объектов class IObject { protected: string _strText; public: virtual void Print() { cout<<"The Object is: "<<_strText.c_str()<<"n"; } }; class Object1: public IObject { public: Object1(){ _strText="Object 1"; } }; class Object2: public IObject { public: Object2(){ _strText="Object 2"; } }; class Object3: public IObject { public: Object3(){ _strText="Object 3"; } }; class Object4: public IObject { public: Object4(){ _strText="Object 4"; } };
  • 52.
    http://www.slideshare.net/IgorShkulipa 52 Object Pool template<unsignedpoolSize> class ObjectPool{ public: static ObjectPool* GetInstance() { if (!_instance) _instance=new ObjectPool(); return _instance; } IObject* GetObject() { for (unsigned i=0;i<poolSize;i++) { if (!_busyObjects[i]){ _busyObjects[i]=true; return _objectPool[i]; } } return NULL; } void ReleaseObject(IObject* object){ for (unsigned i=0;i<poolSize;i++) { if (_objectPool[i]==object){ _busyObjects[i]=false; } } } ...
  • 53.
    http://www.slideshare.net/IgorShkulipa 53 Object Pool ... private: ObjectPool(){ for(unsigned i=0;i<poolSize;i++) { unsigned iObjNumber=rand()%4; switch (iObjNumber) { case 0: _objectPool[i]=new Object1(); break; case 1: _objectPool[i]=new Object2(); break; case 2: _objectPool[i]=new Object3(); break; case 3: _objectPool[i]=new Object4(); break; } _busyObjects[i]=false; } } private: IObject* _objectPool[poolSize]; bool _busyObjects[poolSize]; static ObjectPool* _instance; };
  • 54.
    http://www.slideshare.net/IgorShkulipa 54 Использование ObjectPool template<unsigned poolSize> ObjectPool<poolSize>* ObjectPool<poolSize>::_instance=NULL; int main() { ObjectPool<5>* op=ObjectPool<5>::GetInstance(); IObject* object1=op->GetObject(); if (object1) object1->Print(); else cout<<"The Object is: NULLn"; IObject* object2=op->GetObject(); if (object2) object2->Print(); else cout<<"The Object is: NULLn"; IObject* object3=op->GetObject(); if (object3) object3->Print(); else cout<<"The Object is: NULLn"; IObject* object4=op->GetObject(); if (object4) object4->Print(); else cout<<"The Object is: NULLn"; IObject* object5=op->GetObject(); if (object5) object5->Print(); else cout<<"The Object is: NULLn"; IObject* object6=op->GetObject(); if (object6) object6->Print(); else cout<<"The Object is: NULLn"; IObject* object7=op->GetObject(); if (object7) object7->Print(); else cout<<"The Object is: NULLn"; op->ReleaseObject(object2); IObject* object8=op->GetObject(); if (object8) object8->Print(); else cout<<"The Object is: NULLn"; }
  • 55.
    http://www.slideshare.net/IgorShkulipa 55 Результат The Objectis: Object 2 The Object is: Object 4 The Object is: Object 3 The Object is: Object 1 The Object is: Object 2 The Object is: NULL The Object is: NULL The Object is: Object 4
  • 56.
    http://www.slideshare.net/IgorShkulipa 56 Преимущества инедостатки ◦ Пул объектов отслеживает объекты, которые он создает. ◦ Паттерн Object Pool может использоваться для инкапсуляции логики создания объектов. Однако он не управляет ими после их создания. ◦ Достоинством этого паттерна является быстрое создание объектов, однако это реализовано за счет использования больших ресурсов памяти.
  • 57.
    http://www.slideshare.net/IgorShkulipa 57 Лабораторная работа№16. Порождающие шаблоны 1. Создать класс «Игральная карта» и наследников от него. Создать класс «Колода карт», обрабатывающий выдачу карт игроку на основе паттерна Object Pool. 1. Создать классы «Односвязный список», «Двусвязный список», «Циклический список», на основе общего интерфейса. Реализовать методы доступа к элементам, сортировки и нахождения максимума/минимума. Реализовать создание конкретных списков на основе фабричного метода.