Анимация
JavaScript


Главная  Библионтека 

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 [ 72 ] 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105

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

Основная идея состоит в том, что идентификатор типа и изделие имеют одинаковый тип. На вход фабрики поступает дублируемый объект в виде идентификатора типа, а на выход возвращается новый объект, представляющий собой точную копию этого идентификатора. Точнее говоря, их тип не совсем одинаков: тип IdentifierType из фабрики клонирования- это указатель на объект класса AbstractProduct. На самом деле фабрика получает на вход указатель на клонируемый объект, а возвращает - указатель на клон.

А какие ключи хранятся в ассоциативном массиве? Они не могут быть указателями на объекты класса AbstractProduct, поскольку количество элементов ассоциативного массива не зависит от количества объектов в программе. Каждый элемент этого массива должен соответствовать отдельному типу клонируемого объекта, что вновь приводит нас к стандартному классу std: :type info. Идентификатор типа, передаваемый фабрике клонирования при необходимости создать новый объект, отличается от идентификатора типа, хранящегося в ассоциативном массиве. Это не позволяет нам повторно использовать написанный ранее код. Кроме того, производителю изделия теперь нужен указатель на клонируемый объект. В фабрике, которую мы разработали ранее, параметры были не нужны.

Подведем итоги. Фабрика клонирования получает на вход указатель на объект класса AbstractProduct. Она применяет к этому объекту оператор typeid и получает ссылку на объект класса std: :type info. Затем она ищет этот объект в закрытом ассоциативном массиве. (Функция-член before класса std: :type info устанавливает отношение порядка между объектами этого типа. Это позволяет использовать ассоциативный массив и осуществлять быстрый поиск.) Если элемент не найден, вызывается производитель изделия, которому передается указатель на объект класса AbstractProduct, введенный пользователем.

Поскольку у нас уже есть шаблонный класс Factory, реализация класса CloneFactory упрощается. (Вы можете найти ее в библиотеке Loki.) Класс CloneFactory немного отличается от класса Factory.

• Класс CloneFactory использует класс Typelnfo, а не std: :type info. Класс Typelnfo, рассмотренный в главе 2, представляет собой оболочку указателя на объект класса std: : type i nfo. В нем определены инициализация, оператор присваивания, операторы - и <, необходимые для работы с ассоциативным массивом. Первый оператор переадресовывает вызов оператору класса std: :type info: :operator==, а второй - функции std: :type info: :before.

• В классе больше нет типа identifierType, поскольку идентификатор типа является неявным.

• Шаблонный параметр ProductCreator по умолчанию задает тип AbstractProduct*(*)(AbstractProduct*).

• Класс idToProductMap теперь заменен классом AssocVector<Typelnfo, Productcreator>.

Класс CloneFactory имеет следуюший вид.

template <

class AbstractProduct, class ProductCreator =

AbstractProduct* (*)(AbstractProduct*),



tempiate<typename, class>

class FactoryErrorpolicy = DefaultFactoreError

>

class CloneFactory {

public:

AbstractProduct* CreateObjectCconst AbstractProduct* model); bool RegisterCconst TypeInfo&,

Productcreator creator); bool UnregisterCconst Typelnfo&); private:

typedef Assocvector<Typeinfo, Productcreator>

IdToProductmap; IdToProductMap associations ;

Шаблонный класс CloneFactory представляет собой полное решение задачи клонирования объектов, принадлежаших закрытой иерархии классов (т.е. иерархии, которую невозможно модифицировать). Своей простотой и эффективностью этот класс обязан концепциям, описанным в предыдущих разделах, а также информации о типах, предоставляемой операторами typeid и классом std: :type info. Если бы механизма RTT1 не существовало, фабрику клонирования было намного труднее реализовать.

8.8. Использование фабрики объектов в сочетании с другими обобщенными компонентами

в главе 6 был разработан вспомогательный класс SingletonHolder. Поскольку фабрики имеют глобальную природу, естественно использовать класс Factory вместе с классом SingletonHolder. Их легко объединить с помощью оператора typedef.

typedef SingletonHolder<Factory<shape, std::string> > ShapeFactory;

Разумеется, для уточнения проектных решений в классы SingletonHolder и Factory можно добавлять аргументы, но все это происходит в одном месте. Следовательно, теперь можно собрать фуппу важных решений в одном месте и применить класс ShapeFactory. С помощью простого определения типа, указанного выше, можно выбирать способ функционирования фабрики и синглтона. Единственная строка кода отдает компилятору приказание сгенерировать соответствующий код, аналогично тому, как вызов функции с разными аргументами определяет разные пути ее выполнения. Поскольку в нашем случае все это происходит во время компиляции, основной упор переносится на проектные решения, а не на выполнение профаммы. Разумеется, это не может не влиять на выполнение профаммы, однако это влияние имеет косвенный характер. Когда вы пишете "обычный" код, вы определяете, что будет происходить во время выполнения профаммы. Когда вы пишете определение типа, подобное приведенному выше, вы указываете, что должно произойти во время компиляции профаммы - это можно назвать вызовом функций, генерирующих код.

Как указывалось в начале этой главы, большой интерес вызывает комбинация

класса Factory с классом Functor.

typedef SingletonHolder <

Factory <

Shape, std::string, Functor<Shape*>

>

>

ShapeFactory;



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

8.9. Резюме

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

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

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

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

8.10. Краткий обзор шаблонного класса Factory

• Объявление класса Factory имеет следующий вид.

template <

class AbstractProduct, class IdentifierType,

class ProductCreator = AbstractProduct* C*)C), tempiate<typename, class>

class FactoryErrorPolicy = DefaultFactoryError

>

class Factory;

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

• Класс IdentifierType представляет тип, входящий в иерархию. Он должен иметь отношение порядка, позволяющее хранить его в объекте класса std: :map. Обычно в качестве идентификаторов типа используются сфоки и целые числа.

• Класс ProductCreator представляет собой вызываемую сущность, создающую объект. Этот класс должен поддерживать оператор С), не иметь параметров и возвращать указатель на объекты класса AbstractProduct. Объект класса ProductCreator всегда регисфируется вместе с идентификатором типа.



0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 [ 72 ] 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105