C++ 可配置的类工厂

  项目中常用到工厂模式,工厂模式可以把创建对象的具体细节封装到Create函数中,减少重复代码,增强可读和可维护性。传统的工厂实现如下:

 1 class Widget
 2 {
 3 public:
 4     virtual int Init()
 5     {
 6         printf("Widget Init");
 7         return 0;
 8     }
 9 };
10 
11 class WidgetA : public Widget
12 {
13 public:
14     virtual int Init()
15     {
16         printf("WidgetA Init");
17         return 0;
18     }
19 };
20 
21 class WidgetB : public Widget
22 {
23 public:
24     virtual int Init()
25     {
26         printf("WidgetB Init");
27         return 0;
28     }
29 };
30 
31 
32 class IWidgetFactory
33 {
34 public:
35     virtual Widget *CreateWidget() = 0;
36 };
37 
38 class WidgetFactoryA : public IWidgetFactory
39 {
40 public:
41     virtual Widget *CreateWidget()
42     {
43         Widget *p = new WidgetA();
44         p->Init();
45         return p;
46     }
47 };
48 
49 class WidgetFactoryB : public IWidgetFactory
50 {
51 public:
52     virtual Widget *CreateWidget()
53     {
54         Widget *p = new WidgetB();
55         p->Init();
56         return p;
57     }
58 };
59 
60 
61 int main()
62 {
63     IWidgetFactory *factoryA = new WidgetFactoryA();
64     Widget *widgetA = factoryA->CreateWidget();
65     IWidgetFactory *factoryB = new WidgetFactoryB();
66     Widget *widgetB = factoryB->CreateWidget();
67 
68     return 0;
69 }

  假设有类WidgetA,WidgetB继承自Widget,我们可以创建WidgetFactoryA和WidgetFactoryB,根据需要用factoryA对象或factoryB对象创建对应的对象。这样的方式可以满足大多数的需求。

  现在假如有这样一种需求,我们需要根据配表来生成相应的对象。比如配表中配了值1,希望生成WidgetA,值2,希望生成WidgetB。此时如果还是上述的方法,可能我们只能判断值如果为1,就用factoryA,如果为2则用factoryB。如果有WidgetA-WidgetZ,我们肯定不希望一个个用ifelse做判断。

  因此这里建立一个从type值到对象的工厂映射。只要事先注册好,就可以直接从配表读取数据,并根据type值直接创建对应的对象类型。

 

 1 class WidgetFactoryImplBase;
 2 class WidgetFactory
 3 {
 4 public:
 5     typedef std::map<int, WidgetFactoryImplBase*> FactoryImplMap;
 6     static WidgetFactory &Instance()
 7     {
 8         static WidgetFactory factory;
 9         return factory;
10     }
11 
12     void RegisterFactoryImpl(int type, WidgetFactoryImplBase *impl)
13     {
14         factory_impl_map_.insert(std::make_pair(type, impl));
15     }
16     Widget *CreateWidget(int type);
17 private:
18     FactoryImplMap factory_impl_map_;
19 };
20 
21 class WidgetFactoryImplBase
22 {
23 public:
24     WidgetFactoryImplBase(int type)
25     {
26         WidgetFactory::Instance().RegisterFactoryImpl(type, this);
27     }
28     ~WidgetFactoryImplBase()
29     {}
30     virtual Widget *CreateWidget() = 0;
31 };
32 
33 template<int type, class WidgetType>
34 class WidgetFactoryImpl : WidgetFactoryImplBase
35 {
36 public:
37     WidgetFactoryImpl() : WidgetFactoryImplBase(type)
38     {}
39     ~WidgetFactoryImpl()
40     {}
41     virtual Widget *CreateWidget()
42     {
43         WidgetType *p = new WidgetType();
44         p->Init();
45         return p;
46     }
47 };
48 
49 Widget *WidgetFactory::CreateWidget(int type)
50 {
51     auto it = factory_impl_map_.find(type);
52     if (it == factory_impl_map_.end()) return NULL;
53     return it->second->CreateWidget();
54 }
55 
56 #define DECLARE_WIDGET(type, WidgetType) \
57     static WidgetFactoryImpl<type, WidgetType> o_WidgetFactory_##type
58 
59 DECLARE_WIDGET(0, Widget);
60 DECLARE_WIDGET(1, WidgetA);
61 DECLARE_WIDGET(2, WidgetB);
62 
63 int main()
64 {
65     WidgetFactory::Instance().CreateWidget(1);
66     WidgetFactory::Instance().CreateWidget(2);
67     return 0;
68 }

  由于工厂的Create函数大同小异,首先用模板类来定义特定值对应特定对象的工厂,如果WidgetC的创建过程和一般的不一致,再创建特化类,就省去了对每个对象类写工厂类的过程。然后将这些工厂在构造时自动注册到一个总的WidgetFactory中。真正创建时只需要调用总工厂的Create函数,传入配表等传入的type值,即可创建对应的对象。

  注意这里用了一个DECLARE_WIDGET宏,来绑定type与对应的对象类型。从而将对应的创建工厂注册到总工厂中。

  此方法的逻辑简单,也很好理解,在最近的游戏活动功能中,获得了非常好的效果。由于活动的类型多达几十种,为每一种活动写工厂类和根据配表值做判断会非常繁琐,也容易出错,利用了这样的工厂注册方法后,新加一个活动类型只要加一行注册代码即可搞定,且不会出错。这里把工厂注册机制分享出来,希望对大家有所帮助。

posted @ 2017-01-07 18:26  穆游  阅读(1923)  评论(0编辑  收藏  举报