Программирование >>  Многопоточная библиотека с принципом минимализма 

1 ... 67 68 69 [ 70 ] 71 72 73 ... 106


public:

typedef Shape* C*CreateShapeCanback) () ; private:

typedef std::map<int, CreateShapeCanback> CallbackMap; public:

возвращает значение true, если регистрация прошла успешно bool RegisterShapeCint Shapeld,

CreateShapeCallback CreateFn); возвращает значение true, если фигура Shapeld уже зарегистрирована bool unregisteredShapeCint Shapeld); Shape* CreateShapeCint Shapeld); private:

CallbackMap callbacks.;

Это общая схема масштабируемой фабрики. Фабрика является масштабируемой, поскольку при добавлении нового класса, производного от класса Shape, в код не нужно вносить никаких изменений. Класс shapeFactory разделяет ответственность: каждая новая фигура должна зарегистрироваться в фабрике, вызвав функцию Regis-terShape и передав ей целочисленный идентификатор и указатель на функцию, создающую объект. Обычно эта функция состоит всего из одной строки.

Shape*& CreateLineC)

return new Line;

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

Модуль реализации класса Line

Создаем безымянное пространство имен,

чтобы сделать функцию невидимой из других модулей

namespace

Shape* CreateLineC)

return new Line;

идентификатор класса Line const int LINE =1;

допустим, что фабрика TheShapeFactory - фабрика синглтоновСсм. главу 6) const bool registered =

TheShapeFactory::instanceC).RegisterShapeC line, CreateLine);

Благодаря возможностям, предоставленным стандартным ассоциативным массивом std: :map, класс ShapeFactory реализуется легко. По существу, функции-члены класса ShapeFactory переадресуют аргументы функции-члену callback .

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



bool ShapeFactory:-.RegisterShapeCint shapeld, CreateShapeCallback createFn)

return callback .insert(

CallbackMap::value type(shapeld, createFn)).second;

bool ShapeFactory::unregisterShape(int shapeld) return callbacks .erase(shapeid) == 1;

Если вы не знакомы с шаблонным классом std: :map, предыдуший код нуждается в пояснениях.

Класс std::тар содержит пары, состояшие из ключей и данных. В нашем случае ключами являются целочисленные идентификаторы фигур, а данные состоят из указателя на функцию. Такие пары имеют тип

std::pai r<const int, CreateShapeCal 1 back>. При вызове функции insert нужно передавать ей объект этого типа. Поскольку это выражение слишком длинное, в классе std: :map используется его синоним value type. В качестве альтернативы можно использовать также тип std: :make pai г.

Функция-член insert возвращает другую пару, на этот раз состоящую из итератора (ссылающегося на только что вставленный элемент) и булевской переменной, принимающей значение true, если значения в ассоциативном массиве до этого момента не было, и false - в противном случае.

Функция-член erase возвращает количество удаленных элементов.

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

Shape* ShapeFactory::Createshape(int shapeld)

CallbackMap::const iterator i = callbacks .find(shapeld);

if (i == callbacks .end())

не найден

throw std::runtime error( нeизвecтный идентификатор );

вызываем функцию для создания объекта return (i->second)О;

Посмотрим, что дает нам этот простой класс. Вместо громоздкого оператора swi tch мы получили динамическую схему, требующую, чтобы каждый тип регистрировался в фабрике. Это распределяет ответственность между классами. Теперь, определяя новый класс, производный от класса shape, можно просто добавлять файлы, а не модифицировать их.

8.4. Идентификаторы типов

Осталась одна проблема - управление идентификаторами типов. По-прежнему добавление новых идентификаторов типов требует дисциплинированности и централизованного контроля. Добавляя новый класс, программист обязан проверять все существующие идентификаторы типов, чтобы новый идентификатор не совпал со старыми. Если все же совпадение произошло, второй вызов функции-члена Reg-Глава 8. Фабрики объектов 225



isterShape с тем же самым идентификатором не выполняется, и объект этого типа не создается.

Эту проблему можно решить, выбрав для идентификаторов более мошный тип, чем int. В нашем проекте тип int вовсе не требуется. Нужны лишь типы, которые могуг быть ключами ассоциативного массива, т.е. типы, поддерживающие операторы == и <. (Вот почему следует применять ассоциативные массивы, а не векторы.) Например, идентификаторы типов можно хранить в виде строк, считая, что каждый класс представлен своим именем: идентификатором типа Line является строка Line , типа Polygon - строка Polygon и т.д. Это минимизирует вероятность совпадения идентификаторов, поскольку имена классов уникальны.

Если вы проводите каникулы, изучая язык С++, предыдущий парафаф будет для вас предупредительным сигналом. Давайте попробуем применить класс type info! Класс std: :type info является частью информации о типах времени выполнения программы (Runtime Туре Infomiation - RTT1), предусмотренных языком С++. Ссьшку на класс std: :type info можно получить, применив оператор typeid к типу или выражению. Класс std: :type info содержит функцию-член name, возвращающую указатель типа const char*, ссьыающийся на имя типа. Указатель, возвращаемый оператором typeidCLine) . nameC), ссьыается на строку class Line , что и требовалось.

Проблема состоит в том, что не все компиляторы поддерживают этот оператор. Способ, которым реализована функция type info: iname, позволяет применять ее лгапь для отладки. Нет никакой гарантии, что данная строка действительно представляет собой имя класса, и, что еще хуже, нет никакой гарантии, что эта строка является единственной в рамках приложения. (Да, пользуясь функцией std:: type info::name, вы можете получить два класса с одним и тем же именем.) И совсем убийственный аргумент: нет гарантии, что имя типа постоянно. Никто не может обещать, что оператор typeidCLine).nameC) вернет то же самое имя при повторном выполнении программы. Живучесть реализации - важное качество фабрик, а функция std: :type info: :name является неустойчивой. Итак, хотя класс std: :type info на первый взгляд подходит для создания фабрик, на самом деле он совершенно неприемлем.

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

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

Фабрика СОМ-объектов, разработанная компанией Microsoft, использует именно такой подход. Она содержит алгоритм, генерирующий уникальный 128-битовый идентификатор, называемый глобальным уникальным идентификатором (Global Unique Identifier - GUID) СОМ-объектов. Этот алгоритм основан на уникальности серийного номера сетевой карты или при отсутствии карты даты, времени и других переменных параметров, характеризующих состояние компьютера.



1 ... 67 68 69 [ 70 ] 71 72 73 ... 106

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика