virtual A* createA(); virtual B* createB(); virtual C* createC();
因為 A, B, C 各自有自己的子類 A1/2/3, B1/2/3, C1/2/3 等,而且存在某條件限制 A1, B1, C1 一定要同時搭配使用,所以實作 Factory1 固定只會製造 A1, B1, C1,就可以在產生 Factory1 的同時保證「A1, B1, C1 一定搭配使用」。如果需要換成 A2, B2, C2 的組合,把 Factory1 換成 Factory2 就可以了。
也就是說,factory 跟 abstract factory 的產品不同,選擇要製造哪一種產品的時間也不同。Factory 是製造的產品的時候選擇,但是 abstract factory 在建立 factory 的時候,就已經決定要製造哪種產品。
這樣可以回答一個問題:能否把多個 factory 合併成一個 abstract factory?因為選擇產品的時間不同,所以本質就不同,沒辦法合併。考慮極端的情況,abstract factory 可能只有一種產品 A,也還是跟 factory 不同。另外,前一章的 template Factory 其實有個必須修改產品的缺點,雖然可以從外部改,但是每個產品都要改。這章的 template AbstractFactory 就完全不需要改產品,是不錯的設計。
這章使用的範例是遊戲裡面的敵人,分成 Soldier/Monster 不同類,遊戲本身又分成 easy/hard 兩種模式,所以在 Easy 模式下面,SillySoldier/SillyMonster 會一起出現。套用 abstract factory 的方式,可以在選擇難度的時候產生 EasyEnemyFactory,使得之後的遊戲一定會產生 SillySoldier/SillyMonster。
Summary
在 abstract factory 裡面,有兩個不同的 class,一個是 AbstractFactory 提供製造的界面,另一個是 ConcreteFactory 繼承 AbstractFactory 並提供製造的實作。因此必須定義這兩個 template class。AbstractFactory 的參數是產品的抽象型別,用 Typelist 輸入,就會產生所有 virtual Type* create<AbstractProduct>() 的界面。typedef AbstractFactory<TYPELIST_2(Soldier, Monster)> AbstractEnemyFactory;
typedef ConcreteFactory<
AbstractEnemyFactory,
TYPELIST_2(SillySoldier, SillyMonster)>
EasyEnemyFactory;
AbstractEnemyFactory* pFactory = new EasyEnemyFactory;
Soldier* pSoldier = pFactory->create<Soldier>();
pSoldier->speak();
至於 ConcreteFactory 的參數,就要給相對的 AbstractFactory 來繼承,並且給具體的產品型別,才能夠製造正確的產品。
實作的部份,AbstractFactory 使用 GenScatterHierarchy 多重繼承得到 create<A>(), create<B>() 等抽象界面,然後 ConcreteFactory 使用 GenLinearHierarchy 線性繼承來實作 create<A>(), create<B>() 這些具體實作。要特別注意,繼承的時候同名的 function 會有 hiding 的問題,但如果是 override 父類別的 virtual function 則沒有問題。
書中提到另外一種典型的 AbstractFactory 實作 PrototypeFactory。一樣是繼承 AbstractFactory 並且實作 create<T>,但是 Prototype 使用 clone() 來製造新的產品。既然是 clone() 就需要增加 member function setPrototype(),但是 C++ 繼承結構裡面有同名 member hiding 的問題,書中使用 friend global function 來避免。
更好的方法是用 cast 解決 hiding,這也是 C++ hiding 機制的標準解法。要取得 recursive template 的真正型別有點困難,需要 AbstractFactory 提供正確的 typedef。
public:
// template wrapper function only instanciates in the most derived class
template<class T>
void setPrototype(T* pProto)
{
// calling hiding member function by the exact class name
this->BaseCloneUnit<T>::Result::doSetPrototype(pProto);
}
// member function that is hiding by the most derived class
void doSetPrototype(AbstractProduct* pProto)
{
m_prototype = pProto;
}
protected:
// this template depends on AbstractProductList and AbstractFactoryType
// in AbstractFactory
template<class T>
struct BaseCloneUnit
{
typedef typename Tail<typename Reverse<
typename Base::AbstractProductList>::Result, T>::Result TL;
typedef CloneUnit<T, GenLinearHierarchy<
typename TL::Tail,
CloneUnit,
typename Base::AbstractFactoryType> > Result;
};
private:
// member data that can be accessed in member function
AbstractProduct* m_prototype;
使用結果如下,注意 setPrototype<Monster>() 要指定抽象產品,不然沒辦法轉出正確的型別。因為不是同一個 class overloading,所以必需要區分。
typedef AbstractFactory<TYPELIST_2(Soldier, Monster)> AbstractEnemyFactory;
typedef ConcreteFactory<AbstractEnemyFactory,
TYPELIST_2(Soldier, Monster), CloneUnit> PrototypeEasyEnemyFactory;
PrototypeEasyEnemyFactory* pPrototypeFactory = new PrototypeEasyEnemyFactory;
AbstractEnemyFactory* pFactory = pPrototypeFactory;
pPrototypeFactory->setPrototype<Monster>(new SillyMonster);
pPrototypeFactory->setPrototype<Soldier>(new SillySoldier);
Soldier* pSoldier = pFactory->create<Soldier>();
狂想:如果用 C++11 variable template argument 或者 proprocessor macro 也許就不需要 Typelist + GenXXXHierarchy 這麼複雜的組合了。
No comments:
Post a Comment