C++ 可配置的类工厂

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

 class Widget
{
public:
virtual int Init()
{
printf("Widget Init");
return ;
}
}; class WidgetA : public Widget
{
public:
virtual int Init()
{
printf("WidgetA Init");
return ;
}
}; class WidgetB : public Widget
{
public:
virtual int Init()
{
printf("WidgetB Init");
return ;
}
}; class IWidgetFactory
{
public:
virtual Widget *CreateWidget() = ;
}; class WidgetFactoryA : public IWidgetFactory
{
public:
virtual Widget *CreateWidget()
{
Widget *p = new WidgetA();
p->Init();
return p;
}
}; class WidgetFactoryB : public IWidgetFactory
{
public:
virtual Widget *CreateWidget()
{
Widget *p = new WidgetB();
p->Init();
return p;
}
}; int main()
{
IWidgetFactory *factoryA = new WidgetFactoryA();
Widget *widgetA = factoryA->CreateWidget();
IWidgetFactory *factoryB = new WidgetFactoryB();
Widget *widgetB = factoryB->CreateWidget(); return ;
}

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

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

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

 class WidgetFactoryImplBase;
class WidgetFactory
{
public:
typedef std::map<int, WidgetFactoryImplBase*> FactoryImplMap;
static WidgetFactory &Instance()
{
static WidgetFactory factory;
return factory;
} void RegisterFactoryImpl(int type, WidgetFactoryImplBase *impl)
{
factory_impl_map_.insert(std::make_pair(type, impl));
}
Widget *CreateWidget(int type);
private:
FactoryImplMap factory_impl_map_;
}; class WidgetFactoryImplBase
{
public:
WidgetFactoryImplBase(int type)
{
WidgetFactory::Instance().RegisterFactoryImpl(type, this);
}
~WidgetFactoryImplBase()
{}
virtual Widget *CreateWidget() = ;
}; template<int type, class WidgetType>
class WidgetFactoryImpl : WidgetFactoryImplBase
{
public:
WidgetFactoryImpl() : WidgetFactoryImplBase(type)
{}
~WidgetFactoryImpl()
{}
virtual Widget *CreateWidget()
{
WidgetType *p = new WidgetType();
p->Init();
return p;
}
}; Widget *WidgetFactory::CreateWidget(int type)
{
auto it = factory_impl_map_.find(type);
if (it == factory_impl_map_.end()) return NULL;
return it->second->CreateWidget();
} #define DECLARE_WIDGET(type, WidgetType) \
static WidgetFactoryImpl<type, WidgetType> o_WidgetFactory_##type DECLARE_WIDGET(, Widget);
DECLARE_WIDGET(, WidgetA);
DECLARE_WIDGET(, WidgetB); int main()
{
WidgetFactory::Instance().CreateWidget();
WidgetFactory::Instance().CreateWidget();
return ;
}

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

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

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

上一篇:npm install报错Error: ENOENT


下一篇:KVC与KVO的进阶使用