template的伟大魅力在于它的自动生成代码能力,基于template的技术,无论多么高深仰或多么花哨,只要你明白它的自动生成代码的本质,问题会好处理很多。
譬如AbstractFactory在template下的loki做法,它只是为了让编译器帮我们生成代码而已,如果你考虑手写代码,问题会好理解很多。
A抽象基类问题
如果你要使用AbstractFactory模式,那你首先要有一个基类,它定义了生成方法。Loki定义了如此一个接口:
class AFUnit
... {
public:
virtual T* DoCreate(Type2Type<T>) = 0;
virtual ~AFUnit();
} ;
这是一个模板化的东西,明显,在不同的类参与作用下,这会被实例化出一系列类,这是与AF有悖的,毕竟AF我们希望使用统一一个基类,为了支持到这一点,Loki使用了强大的外星动力:GenScatterHierarchy。如此我们得到了一个统一的唯一的接口:
<
class TList,
template < class > class Unit = AFUnit
>
class AbstractFactory : public GenscatterHierarchy < TList,Unit >
... {
public:
typedef TList ProductList;
template <class T>
T* Create()
...{
Unit<T>& unit = *this;
return unit.DoCreate(Type2Type<T>());
}
} ;
尽管这个AbstractFactory是一个多类的组合体,只是它可以作为一个类使用处理,但这不仅仅保证了一个唯一的基类,还给我们提供了额外的优势——细粒度化,你可以对不同的模块传递它的不同颗粒,本模块不必知晓任何一点与自己无关的颗粒以及与该颗粒管理的类、包含关系。
我们回顾AF模式,它要求基类只需要提供统一接口。ok,这个AbstractFactory已经满足这个要求了。使用AF的时候抽象基类问题到此得到完美解决。
B子类实现问题
AF有几个要求,子类要继承基类,并且实现基类的方法,子类也应该是一个唯一的类。
基类已经是一个多颗粒的东西,子类必然是多颗粒的,单又要都继承于AbstractorFactory而且又要保证自己的外观是单颗粒的,只有逐一继承最后包装,GenLinearHierarchy可以帮上这个忙,它可以产出都继承于某一类的类(如果你习惯使用loki的时候使用默认参数,我建议你要多全部参数的声明有个了解),而且它可以对TList操作,不要忘记,AbstractFactory也使用了TList,使用同样的TList可以使你得到完美的继承体系。
满足了继承关系,还要重写基类的虚函数,使用policy是个不错的选择,为继承体系中的每个类都加入一个实现了虚函数的policy,这样每个类都有了虚函数的实现,而每个类又恰恰继承于基类,这简直太完美了,巧合还是人为?难以一言蔽之。
template < class , class , > class Creator = OpNewFactoryUnit
class TList = typename AbstractFact::ProductList
>
class ConcreteFactory
: public GenLinearHierarchy < typename TL::Reverse < TList > ::Result,Creator,AbstractFact >
... {
public:
typedef typename AbstractFact::ProductList ProductList;
typedef TList ConcreteProductList;
} ;
使用TL和Creator创建继承于AbstractFact的线性类表,而ConcreteFactory继承于这个线性表,或者可以说ConcreteFactory是对多颗粒的一个单粒度封装。还是太完美了。
到这里实现Creator规则太简单了。
class OpNewFactoryUnit : public Base
... {
typedef typename Base::ProductList BaseProductList;
protected:
typedef typename BaseProductList::Tail ProductList;
public:
typedef typename BaseProductList::Head AbstractProduct;
ConcreteProduct* DoCreate(Type2Type<AbstractProduct>)
...{
return new ConcreteProduct;
}
} ;
Creator没有什么特别的,按照指定规则做事而已。
到这里AF已经被Loki包装成完全自动生成代码的东西了,你只需提供少量的信息,就可以批量获取产品,这才是工厂。
// MyKit* pKit = new MyKit;
// Test2* t2 = pKit->Create<Test2>();
typedef ConcreteFactory < MyKit > MyConKit;
MyConKit * pKit = new MyConKit;
SmartPtr < Test2 > t2 = pKit -> Create < Test2 > ();
t2 -> say();
SmartPtr < Test1 > t3 = pKit -> Create < Test1 > ();
t3 -> say();
不管如何美化,template还是为了生成代码,这是它的本质,用这个本质考虑template参与情况下的问题会容易理解很多。分析使用AF的时候的逐个细节并归纳,你也可以完成这些。