Анимация
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

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

8.5. Обобщение

Перечислим элементы, которые мы упомянули, обсуждая фабрики объектов. Это даст нам пищу для размышлений при создании обобщенной фабрики объектов.

• Конкретное изделие (concrete product). Фабрика производит изделие в виде объекта.

• Абстрактное изделие (abstract product). Изделие создается на основе наследования базового типа (в нашем примере - класса Shape). Изделие - это объект, тип которого принадлежит определенной иерархии. Базовый тип этой иерархии представляет собой абстрактное изделие. Фабрика обладает полиморфным поведением, т.е. она возвращает указатель на абстрактное изделие, не передавая знания о типе конкретного изделия.

• Идентификатор типа изделия (product type identifier). Это объект, идентифицирующий тип конкретного изделия. Как уже указывалось, идентификатор типа необходим для создания изделия, поскольку система типов языка С++ является статической.

• Прозводитель изделия (product creator). Функция или функтор специализируются на создании одного, точно заданного типа объектов. Производитель изделия моделируется с помощью указателя на функцию.

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

На первый взгляд все перечисленные выше элементы можно преобразовать в шаблонные параметры класса Factory. Однако есть одно исключение: конкретное изделие не обязано быть известным фабрике. Если бы мы должны были точно указывать тип конкретного изделия, то получили бы классы Factory для каждого конкретного изделия отдельно. Это противоречит нашей цели - изолировать класс Factory от конкретных типов. Тип фабрики должен зависеть только от абстрактного изделия.

Итак, подведем промежуточный итог в виде следующего шаблона.

template <

class AbstractProduct, typename identifierType, typename ProductCreator

>

clsss Factory {

public:

bool RegisterCconst IdentifierType* id, ProductCreator creator) {

return associations .insert(

AssocMap::value typeCid, creator)).second;

bool UnregisteredCconst IdentifierType* id) {

return associations .eraseCid) == 1;



AbstractProduct* CreateObjectCconst identifierTypeA id) {

typename AssocMap::const iterator i =

associations findCid); if (i != associations..endO) {

returnCi->second)();

обработка ошибок

private:

typedef std::map<identifierType, AbstractProduct>

AssocMap; AssocMap associations.;

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

Наша обобщенная фабрика должна давать пользователю возможность настраивать ее на любое из перечисленных выше действий и предусматривать разумное поведение по умолчанию. Следовательно, код, предназначенный для обработки ошибок, должен быть выделен из функции-члена createobject в отдельную стратегию FactoryError (глава 1). Эта стратегия состоит только из одной функции OnunknownType, а класс Factory предоставляет этой функции шанс (и соответствующую информацию) для принятия любого разумного решения.

Стратегия FactoryError очень проста. Она представляет собой шаблонный класс с двумя параметрами: identifierType и AbstractProduct. Если класс FactoryEr-rorimpl представляет собой реализацию стратегии FactoryError, должно применяться следующее выражение.

FactoryErrorimp1<identifierType, AbstractProduct> factoryErrorimpI; IdentifierType id;

AbstractProduct* pProduct = factoryErrorlmpT.OnUnknownTypeCid);

Класс Factory использует класс FactoryErrorimpI в качестве спасательного круга: если класс createobject не может найти ассоциацию в своем внутреннем ассоциативном массиве, он использует функцию-член FactoryEr-rorimpT<identifierType, AbstractProduct>::OnunknownType для извлечения указателя на абстрактное изделие. Если функция OnunknownType генерирует исключительную ситуацию, она передается за пределы класса Factory. В противном случае функция Createobject просто возвращает результат выполнения функции OnunknownType.

Попробуем закодировать эти дополнения и изменения (выделенные в тексте профаммы полужирным шрифтом).

template <

class AbstractProduct, typename IdentifierType, typename productcreator, tempTate<typename, cTass>



class FactoryErrorPolicy

Давать этому классу другое имя (например FactoryException) нет никакой необходимости, поскольку этот тип определен внутри шаблонного класса DefaultFactoryError.

class Factory

: public FactoryErrorPolicy<ldentifierType, AbstractProduct>

public:

AbstractProduct* CreateObjectCconst IdentifierType* id)

typename AssocMap::const iterator i =

association .findCid); if (i != associations .endC)) {

return (i->second)C);

return OnunknownTypeCid);

private:

... остальные функции и данные остаются неизменными ...

По умолчангао реализация стратегаи Facto гуЕ г го г генерирует исюпочительную ситуацию. Класс исключительной ситуации должен отличаться ото всех других типов, чтобы клиентский код мог распознать его и принять соответствующие рещения. Кроме того, этот класс должен быть производным от одного из стандартных классов исключительных ситуаций, чтобы клиентский код мог перехватывать все виды ощибок в одном блоке catch. Стратегия DefaultFactoryError содержит вложенный класс исключительной ситуации (с именем Exception)"*, производный от класса std::exception.

template <class IdentifierType, class ProductType> class DefaultFactoryError

public:

class Exception : public std::exception {

public:

ExceptionCconst IdentifierType* unknownid) : unknownid Cunknownld)

virtual const char* whatC) {

return "Фабрике передается неизвестный тип.";

const IdentifierType GetldC) {

return unknownld ;

private:

IdentifierType unknownld ;

protected:

StaticProductType* onunknownType(const IdentifierType* id) throw Exception(id);



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