1. 程式人生 > >【C/C++開發】C++ 可配置的類工廠

【C/C++開發】C++ 可配置的類工廠

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與對應的物件型別。從而將對應的建立工廠註冊到總工廠中。

  此方法的邏輯簡單,也很好理解,在最近的遊戲活動功能中,獲得了非常好的效果。由於活動的型別多達幾十種,為每一種活動寫工廠類和根據配表值做判斷會非常繁瑣,也容易出錯,利用了這樣的工廠註冊方法後,新加一個活動型別只要加一行註冊程式碼即可搞定,且不會出錯。這裡把工廠註冊機制分享出來,希望對大家有所幫助。