Xavier's Blog

Typelist && Abstract Fatory

| Comments

上一篇博客讲到了模板元编程中的Typelist,这种技术能够让编译器帮你生成许多结构类似的代码,省去了程序员自己编写代码的时间以及一些运行时的效率损失。设计模式中的Abstract Factory,其Template MetaProgramming版本也依赖于TypeList技术。

普通实现

Abstract Factory模式,即规定了一组抽象产品(Abstract Product)的接口,再由各个具体的Factory生成不同组的具体产品(Concrete Product)。我一下子想到了公司的RD、QA以及PM,就以这三种角色为例吧。(虽然不是很恰当,因为不同级别的不同职位是可以共存的)

普通的代码一般会写成这样:

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
class AbstractFactory
{
public:
    virtual RD* create_RD() = 0;
    virtual QA* create_QA() = 0;
    virtual PM* create_PM() = 0;
};

class JuniorFactory: public AbstractFactory
{
public:
    RD* create_RD() {return new JuniorRD();}
    QA* create_QA() {return new JuniorQA();}
    PM* create_PM() {return new JuniorPM();}
};

class SeniorFactory: public AbstractFactory
{
public:
    RD* create_RD() {return new SeniorRD();}
    QA* create_QA() {return new SeniorQA();}
    PM* create_PM() {return new SeniorPM();}
};

//使用方代码
AbstractFactory* fact = NULL;
if (/**/) {
    fact = new JuniorFactory();
} else (/**/) {
    fact = new SeniorFactory();
}

RD* rd = fact->create_RD();
//...

可以看出来,每一个具体的Factory类只是重复的实现AbstractFactory提供的接口,唯一的不同就是填写具体的类名,其它的语句都是重复的。(我不得不在编写以上代码的时候使用了Vim的替换功能)

定义接口

有了Typelist,我们可以联想到,如果有了一个Typelist例如:

1
TypeList<RD, TypeList<QA, TypeList<PM, NullType> > >

再加上一个模板函数:

1
2
3
4
5
template <typename T>
T* create()
{
    return new T();
}

应该就可以生成一组接口了吧!但是有个问题,如何根据Typelist中的每一个类,生成一个对应的接口呢?这里用到了一种技巧,能够根据Typelist,生成一个松散结构的复杂类,这个复杂类中就声明了一组接口。其代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/*
 * Scatter Hierarchy
*/

template<class TypeList, template<class> class Unit>
class ScatterHierarchy;

template<class Head, class Tail, template<class> class Unit>
class ScatterHierarchy<TypeList<Head, Tail>, Unit>
    : public ScatterHierarchy<Head, Unit>
    , public ScatterHierarchy<Tail, Unit>
{};

template<class SingleType, template<class> class Unit>
class ScatterHierarchy: public Unit<SingleType>
{};

template<template<class> class Unit>
class ScatterHierarchy<NullType, Unit>
{};

ScatterHierarchy类型,其模板参数有两个,一个是Typelist,这个很明显,第二个的语法有点怪,是一个template template parameter,他是一种类型,并且这个类型也是一个模板类,就是他规定了接口。假设实际应用中,Unit的定义是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
template<class T>
struct Type2Type
{
    typedef T OriginType;
};

template<class T>
class AbstractFactoryUnit
{
public:
    virtual T* doCreate(Type2Type<T>) = 0;
    virtual ~AbstractFactoryUnit() {};
};

这样如果代入Typelist的话,我们就会得到三个AbstractFactoryUnit基类,分别是:AbstractFactoryUnit<RD>AbstractFactoryUnit<QA>以及AbstractFactoryUnit<PM>,他们都有一个doCreate接口。Wait a minute,那个Type2Type是做什么的?里面不就是typedef了一下嘛!这个其实是一个tricky的技巧:让不同接口的doCreate函数有不同的函数签名。这样当我调用doCreate接口的时候,就能让编译器确定接口是哪个Type(RD、QA or PM)的了。

好了,有了这些基础,就可以开始定义AbstractFactory了,其实我们仅需要继承上面的ScatterFactory即可,然后将具体的TypeList和AbstractFaUnit作为模板参数即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
template
<
typename Tlist,
template<typename> class Unit = AbstractFactoryUnit     /*默认模板参数*/
>
class AbstractFactory: ScatterHierarchy<Tlist, Unit>
{
public:
    typedef Tlist ProductList;
    template<typename T>
        T* create()
        {
            Unit<T>* unit = this;   //AbstractFactory是任意一个Unit<T>的子类, for T in Tlist
            return unit->doCreate(Type2Type<T>());
        }
};

//使用方代码,定义了三个接口的AbstractFactory
typedef AbstractFactory
<
TypeList<PM, TypeList<RD, TypeList<QA, NullType> > >
>
AbstractSoftwareEngineerFactory;

实例化

现在来想想看怎么实例化一个抽象工厂。需要这样三部分元素:

  • 抽象接口(AbstractSoftwareEngineerFactory提供)
  • 实例Typelist(TypeList<JuniorRD, TypeList<JuniorQA, Typelist<JuniorPM, NullType> > >
  • 实例函数(当用户代码为create<RD>()时,能够new JuniorRD();

前两部分都比较容易提供,第三部分的实例函数怎么提供呢?怎么能够一一对应上呢?初步的想法是:AbstractSoftwareEngineerFactory中有Abstract Product(RD / QA / PM),实例Typelist中有Concrete Product(Junior RD / Junior QA / Junior PM),能够把这两种Typelist中的第一个类型(TypeList::Head)对应起来,并且实现了doCreate接口,接着再用递归的思想将剩下的Type依次实现即可!

模板元编程中的递归思想,其表现形式我想到了两种:1)类继承;2)模板特化。另外还需考虑到的是,这种递归应该是一种线性式的,因为需要按顺序遍历Typelist。与之前提到过的ScatterHierarchy实现类似,还有一种技术能够生成一种线性式的类继承关系——LinearHierarchy

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
/*
 * Linear Hierarchy
 */

template
<
    class Tlist,
    template<class SingleType, class Base> class Unit,
    class Root = NullType
>
class LinearHierarchy {};

template
<
    class Head,
    class Tail,
    template<class, class> class Unit,
    class Root
>
class LinearHierarchy<TypeList<Head, Tail>, Unit, Root>
    : public Unit< Head, LinearHierarchy<Tail, Unit, Root> > {};    //Head被“提取”出来了

template
<
    class T,
    template<class, class> class Unit,
    class Root
>
class LinearHierarchy<TypeList<T, NullType>, Unit, Root>
    : public Unit<T, Root> {};

如果,我们将Tlist设置为Concrete Product的Typelist,Root中含有Abstract Product的Typelist,这样就不难找到对应关系了。别忘了,AbstractSoftwareEngineerFactory中就含有Abstract Product,Root用它就好了。

那么对于RD / QA / PM的例子,生成的类之间的继承关系就会如下所示(最上面是基类):

   ASF
    |
Unit<PM, ASF>
    |
LinearHierarchy<TypeList(PM), ASF>
    |
Unit<QA, LinearHierarchy<TypeList(PM), ASF>
    |
LinearHierarchy<TypeList(QA, PM), ASF>
    |
Unit<RD, LinearHierarchy<TypeList(QA, PM), ASF>
    |
LinearHierarchy<TypeList(RD, QA, PM), ASF>
    |
ConcreteFactory

对于Unit,我们可以这样实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
template<class ConcreteProduct, class Base>
class OpNewUnit: public Base
{
private:
    //AbstractSoftwareEngineerFactory中的TypeList
    typedef typename Base::ProductList BaseProductList;

protected:
    //将Abstract Product中的Head“吃掉”,将Tail作为整个Typelist传至子类,精妙!
    typedef typename BaseProductList::Tail ProductList;

public:
    typedef typename BaseProductList::Head AbstractProduct;

    ConcreteProduct* doCreate(Type2Type<AbstractProduct>)
    {
        return new ConcreteProduct();
    }
};

最后,终于可以实例化一个工厂模板类了,只需继承一个含有以上三部分元素(抽象接口、具体TypeList、实例函数)的LinearHierarchy模板类就可以了:

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
template
<
    class AF,
    class ConcreteProductTypeList,
    template<class, class> class Creator = OpNewUnit
>
class ConcreteFactory: public LinearHierarchy<typename Reverse<ConcreteProductTypeList>::Result, Creator, AF>
{
public:
    typedef typename AF::ProductList ProductList;
    typedef ConcreteProductTypeList ConcreteProductList;
};

//使用方代码
typedef ConcreteFactory
<
    AbstractSoftwareEngineerFactory,
    TypeList<JuniorRD, TypeList<JuniorQA, TypeList<JuniorPM, NullType> > >
>
    JuniorFactory;

typedef ConcreteFactory
<
    AbstractSoftwareEngineerFactory,
    TypeList<SeniorRD, TypeList<SeniorQA, TypeList<SeniorPM, NullType> > >
>
    SeniorFactory;

AbstractSoftwareEngineerFactory* junior_fact = new JuniorFactory;
PM* pm = junior_fact->create<PM>(); //"junior PM"
cout << pm->get_title() << endl;

AbstractSoftwareEngineerFactory* senior_fact = new SeniorFactory;
RD* rd = senior_fact->create<RD>(); //"seinor RD"
cout << rd->get_title() << endl;

可能你会看到,上面的代码用到了Reverse,把使用方的ConcreteProductList给反转过来,这是为什么呢?可以再看下OpNewUnit的实现,它是将AbstractProductList中的Head去掉之后,将剩下的部分传递至子类,而根据LinearHierarchy的实现,在ConcreteProductList中越是头部的类,越是在子类中,所以需要将其中一个ProductList给反转过来,以便让OpNewUnit中的BaseProductList::Head和模板参数ConcreteProduct能够对应起来。关于Reverse的实现,可以查看上一篇文章的最后部分。

参考资料

— EOF —

Comments