Анимация
JavaScript
|
Главная Библионтека Шаблон проектирования Prototype избегает этой дилеммы, используя прототип объекта. Имея прототип, мы можем использовать преимущества виртуальных функций. Дилемма виртуального конструктора по отнощению к самому прототипу остается нерешенной, но теперь она носит намного более локальный характер. В описанном выше примере подход, основанный на применении прототипов при создании врагов, задействованных в компьютерной игре, вынуждает использовать указатели на объекты базовых классов Soldier, Monster и SuperMonster. В этом случае код может выглядеть примерно так. class GameApp { void SelectLevelО { i f (пользователь выбрал уровень повышенной сложности) { protoSoldier .resetCnew BadSoldier); protoMonster .resetCnew BadMonster); protoSuperMonster .resetCnew BadSuperMonster); else { protoSoldier .resetCnew SillySoldier); protoMonster .resetCnew sillyMosnter); protoSuperMonster .resetCnew SillySuperMosnter); Soldier* MakeSoldierC) { в каждом вражеском классе определяется виртуальная функция Clone return pProtoSoldier ->CloneC); ... классы MakeMonster и MakeSuperMonster определяются аналогично private: используем эти прототипы для создания врагов auto ptr<Soldier> protoSoldier ; auto ptr<Monster> protoMonster ; auto ptr<superMonster> protoSuperMonster ; Разумеется, в реальном коде лучше было бы разделить интерфейс и реализацию. Основная идея заключается в том, чтобы класс GameApp хранил указатели на базовые вражеские классы ~ прототипы. Класс GameApp использует эти прототипы для создания вражеских объектов, применяя к ним виртуальную функцию Clone. Реализация шаблона Abstract Factory, основанная на применении прототипов, может хранить указатель на каждый тип изделия и использовать для создания новых изделий функцию С1 one. В классе ConcreteFactory, использующем прототип, больше нет необходимости предусматривать конкретные типы. В нашем примере для создания объектов классов SillySoldier и BadSoldier нужно лишь передать фабрике соответствующие прототипы. Статический тип прототипа - базовый класс Soldier. Фабрика не обязана ни- Учтите, этот код неверно обрабатывает исключительные ситуации. Исправьте его самостоятельно. чего знать о конкретных типах объектов. Она просто вызывает виртуальную функцию-член Clone из соответствующего прототипа. Это ослабляет зависимость конкретных фабрик от конкретных типов. Однако, для того чтобы механизм класса GenLinearHierarchy работал правильно, нужен список типов. Напомним, как выглядит объявление класса ConcreteFactory. template < class AbstractFact, template <class, c1ass> class Creator, class TList > class ConcreteFactory; Класс TList- это список конкретных изделий. В классе EasylevelEnemyFactory класс TList был известен под именем TYPELIST 3 (SillySoldier, SillyMonster, SillySuperMonster). При использовании щаблона проектирования Pattern класс TList становится неуместным. Однако класс TList по-прежнему необходим классу GenLinearHierarchy для генерации отдельного класса для каждого изделия, указанного в списке абстрактных изделий. Что делать? В этой ситуации естественно передать список абстрактных изделий классу ConcreteFactory в качестве аргумента TList. Теперь класс GenLinearHierarchy сможет генерировать правильное количество классов, а изменять реализацию класса ConcreteFactory не понадобится. Объявление класса ConcreteFactory принимает следующий вид. template < class AbstractFact, template <class, class> class Creator, class TList = typename AbstractFact::ProductList > class ConcreteFactory; Напомним, что определение класса AbstractFact, приведенное в разделе 9.3, содержит внутренний класс ProductList. Перейдем теперь к реализации шаблонного класса PrototypeFactoryUnit, содержащего прототип и вызывающего функцию Clone. Его реализация намного проще, чем реализация класса OpNewFactoryunit, поскольку вместо двух списков типов класс PrototypeFactoryUni t работает только с одним списком абстрактных изделий. template <class ConcreteProduct, class Base> class PrototypeFactoryUnit : public Base { typedef typename Base::ProductList BaseProductlist; protected: typedef typename Base::ProductList TailProductlist; public: typedef typename Base::ProductList::Head AbstractProduct; PrototypeFactoryunit(AbstractProduct* p = 0); : pPrototype (p) friend void DoGetPrototype(const PrototypeFactoryUnit& me, AbstractProduct** pPrototype) pprototype = me.pPrototype ; 250 Часть II. Компоненты riend void DoSetPrototype(PrototypeFactoryunit& me, AbstractProduct* pObj) me.pPrototype =pObj; .emplate <class u> void GetPrototype(AbstractProduct*& p) return DoGetPrototype(*this, p); jemplate <class u> void SetPrototypeCU* pObj) DoSetPrototype(*this, pObj); AbstractProduct* DoCreate(Type2Type<AbstractProduct>) assert(pPrototype ); return pPrototype ->clone(); private: AbstractProduct* pPrototype ; Шаблонный класс PrototypeFactoryunit основан на некоторых предположениях. Во-первых, он не владеет своим прототипом. Можно потребовать, чтобы функция SetPrototype удаляла старый прототип перед его переприсваиванием. Во-вторых, класс PrototypeFactoryunit использует функцию Clone, предположительно клонирующую объект. В конкретном приложении можно использовать другое имя, руководствуясь собственными вкусами или требованиями, предъявляемыми библиотекой. Если нужно настроить фабрику, основанную на применении прототипов, достаточно написать шаблонный класс, аналогичный классу PrototypeFactoryunit. Для этого можно применить механизм наследования, сделав класс PrototypeFactoryunit базовым и замещая соответствующие функции. Допустим, что мы решили реализовать функцию DoCreate так, чтобы она возвращала нулевой указатель, если указатель на прототип равен нулю. template <class AbstractProduct, class Base> class MyFactoryunit : public PrototypeFactoryUnit<AbstractProduct, Base> public: Реализуем функцию DoCreate так, чтобы она допускала нулевой указатель на прототип AbstractProduct* DoCreate(Type2Type<AbstractProduct>) return pPrototype ? pPrototype ->Clone() : 0; Вернемся к нашей компьютерной игре. Для того чтобы определить конкретную фабрику, нам нужно написать следующий код приложения. код приложения typedef ConcreteFactory < AbstractEnemyFactory, PrototypeFactoryunit > EnemyFactory; 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 |