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

• Список типов содержит набор классов, создаваемых фабрикой. Каждый конкретный тип, указанный в списке, соответствует абстрактному типу с тем же индексом в списке типов класса AbstractFactory. Например, класс SillyMonster (индекс 1) представляет собой конкретную разновидность класса Monster, имеющую тот же индекс в определении класса AbstractEnemyFactory.

Описав класс ConsreteFactory, попробуем его реализовать. Немного поразмыслив, легко прийти к выводу, что количество замещений чисто виртуальных функций должно совпадать с количеством определений класса. (В противном случае мы не смогли бы конкретизировать класс ConcreteFactory.) Следовательно, класс ConcreteFactory должен быть производным от класса OpNewFactoryunit, отвечающего за реализацию функции DoCreate.

Здесь больщую помощь может оказать шаблонный класс Genii nearHierarchy (глава 3), поскольку в нем предусмотрены все детали генерации нужных нам реализаций.

Класс AbstractEnemyFactory должен быть корнем иерархии. Все реализации функции DoCreate и окончательный класс EasyLevel EnemyFactory должны быть производными от него. В каждой реализации класса OpNewFactoryunit должна замешаться одна из трех чисто виртуальных функций DoCreate, определенных в классе AbstractEnemyFactory.

Определим класс OpNewFactoryunit. Очевидно, класс OpNewFactoryunit - это шаблонный класс, получаюший тип создаваемого объекта в качестве шаблонного параметра. Кроме того, класс GenLinearHierarchy требует, чтобы класс OpNewFactoryunit получал дополнительный шаблонный параметр и был его потомком. (Класс GenLinearHierarchy использует второй аргумент для генерации линейной (string-shaped) иерархии наследования, изображенной на рис. 3.6.)

template <class ConcreteProduct, class Base>

class OpNewFactoryunit : public Base

typedef typename Base::ProductList BaseProductList; protected:

typedef typename BaseProductList::Tai1 ProductList; public:

typedef typename BaseProductList::Head AbstractProduct; ConcreteProduct* DoCreate(Type2Type<AbstractProduct>) {

return new ConcreteProduct;

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

Каждая конкретизация класса OpNewFactoryunit является компонентом некоей "пищевой" цепи. Каждая конкретизация класса OpNewFactoryunit "откусывает" голову списка изделий, замещая соответствующую функцию DoCreate и передавая "безголовый" список ProductList вниз по иерархии классов. Таким образом, конкретизация класса OpNewFactoryunit, находящаяся на вершине иерархии (сразу за классом AbstractEnemyFactory), реализует функцию DoCreate(Type2Type<Soldier>), а конкретизация, находящаяся на дне иерархии, реализует функцию DoCreate (Type2Type<SuperMonster>).

Итак, попробуем разобраться, почему класс OpNewFactoryunit занимает такое высокое положение в иерархии. Во-первых, класс OpNewFactoryunit импортирует тип



ProductList из базового класса и присваивает ему новое имя BaseProductList. (Просмотрев определение класса AbstractFactory, легко понять, что он на самом деле экспортирует тип ProductList.) Абстрактное изделие, реализованное классом OpNewFactoryunit, представляет собой голову списка BaseProductList в соответствии с определением класса AbstractProduct. В заключение класс OpNewFactoryunit реэкспортирует класс BaseProductList: :Tail в качестве типа ProductList. Оставшаяся часть списка передается вниз по иерархии.

Обратите внимание на то, что функция OpNewFactoryunit: :DoCreate не возвращает указатель на объект класса AbstractProduct, как это делает ее настоящий прототип. Вместо этого она возвращает указатель на объект класса concreteProduct. Можно ли это по-прежнему квалифицировать как реализацию чисто виртуальной функции? Да, благодаря ковариантным типам возвращаемых значений (covariant return types). Язык С++ позволяет возвращать указатель на объект производного класса. В этом есть глубокий смысл. Благодаря этим типам программист либо знает точный тип конкретной фабрики, получая максимум информации, либо знает только ее базовый тип, получая меньший объем информации.

Класс ConcreteFactory должен генерировать иерархию, используя класс GenLinearHierarchy. Его реализация вполне очевидна.

template <

class AbstractFact,

template <class, class> class Creator = OPNewFactoryUnit; class TList = typename AbstractFact::ProductList

>

class ConcreteFactory

: public GenLinearHierarchy< typename TL:Reverse<TList>::Result, Creator, AbstractFact>

typedef typename AbstractFact::ProductList ProductList; typedef TList ConcreteProductList;

Иерархия классов, генерируемая классом GenLinearHierarchy для класса ConcreteFactory, показана на рис. 9.3.

Здесь скрывается одна маленькая хитрость: класс ConcreteFactory должен переворачивать список конкретных изделий, передавая его классу GenLinearHierarchy. Зачем? Для этого нужно вернуться к рис. 3.6, на котором показано, как класс GenLinearHierarchy генерирует иерархию. Этот класс передает типы из списка типов в шаблонный аргумент unit снизу вверх. Первый элемент списка типов передается конкретизации класса unit, находящейся на дне иерархии классов. Однако класс OpNewFactoryunit реализует перегрузку функции DoCreate сверху вниз. Следовательно, класс ConcreteFactory должен переворачивать список TList, используя статический алгоритм TL::Reverse (глава 3), и только после этого передавать его классу GenLi nearHi erarchy.

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



понятнее. Если уж вы на самом деле решили использовать списки типов, то ваши возможности станут поистине неограниченными. Не верите? Читайте дальше.

AbstractEnemyFactory

OpNewFactoryUnit<SillySoldier, AbstractEnemyFactory>

GenUnearHierarchy<?ffEUST 1 (SillySoldier), OpNewFactoryUnit,AbstractEnemyFactory>

OpNewCreator<SillyMonster, GenUnearHierarchy<?ffELIST 1 (SillySoldier), OpNewFactoryUnit, AbstractEnemyFactory»

GenUnearHierarchy<TYPEUST2(SillyMonster, SillySoldier), OpNewCreator,AbstractEnemyFactory>

OpNewFactoryUnitOillySuperMonster, GenLinearHierarchy <TYPEUST2(SillyMonster, SillySoldier), OpNewCreator,AbstractEnemyFactory»

GenLinearHierarchy<TYPELIST 3(SillySuperMonster, SillyMonster, SillySoldier), OpNewFactoryUnit,AbstractEnemyFactory>

EasyLevelEnemyFactory

Рис. 9.3. Иерархия классов, генерируемая для класса EasyLevelEnemyFactory

9.4. Реализация шаблона Abstract Factory на основе прототипов

Шаблон проектирования Prototype (Gamma et al., 1995) описывает метод создания объектов, начиная с npomotnuna, представляюшего собой некий архетип. Новые объекты получаются путем клонирования прототипа. Суть этого способа заключается в том, что функция клонирования является виртуальной.

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



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