在软件开发过程中,经常需要根据不同的条件创建不同类型的对象。例如,在解析XML数据或从数据库加载对象时,可能需要根据数据类型来创建不同的对象实例。传统的实现方式通常涉及到大量的条件判断语句,如switch-case或if-else,这不仅使得代码难以维护,而且编译效率低下。本文将介绍一种使用动态订阅机制的类工厂实现方法,它能够根据自定义标准创建对象,从而简化代码并提高编译效率。
是否曾经编写过一个庞大的switch语句,其唯一的功能就是创建新的对象?是否曾经将所有对象的头文件都包含在一个CPP文件中?然后,当在其中一个头文件中做了一个小小的修改,编译过程却变得异常漫长?下面是一个类似的例子:
CBase* pBase(NULL);
switch (type_variable) {
case obj1: pBase = new CBaseDerivate1(); break;
case obj2: pBase = new CBaseDerivate2(); break;
...
case objN: pBase = new CBaseDerivateN(); break;
}
或者更糟糕的是:
CBase* pBase(NULL);
if (!strcmp(string_for_type, "Type1")) pBase = new CBaseDerivate1();
else if (!strcmp(string_for_type, "Type2")) pBase = new CBaseDerivate2();
...
else if (!strcmp(string_for_type, "TypeN")) pBase = new CBaseDerivateN();
当使用复杂条件时,这种情况可能会变得更糟。
在实现XML解析器和从数据库加载存储对象时,遇到了很多类似的构造。很快就厌倦了这种做法。随着经验的增长,开始思考这个问题。最近,当再次需要使用类似的代码时,找到了以下方法...
实际上想要的是一个“类工厂”,它可以为创建类。
CBase* pBase = CClassFactory::CreateInstance(type_variable);
可以将对象构造隐藏在一个单独的类工厂中。在ATL中,类工厂使用一个静态数组来存储可用的类,并指向CreateInstance函数。新条目由向导添加。但是,头文件和编译问题(仍然有很多包含会进入CPP)仍然存在。
然后,想到了一个主意——为什么不使用静态数组/映射?但是动态填充?所有的类都会自己“注册”它们的创建者函数。这是否可能?如何实现?
最初,静态数组/映射动态填充的想法听起来非常奇怪,但是声明std::map/vector作为静态的,并在稍后通过某种巧妙的机制填充它...是的,可以做到。
现在,对于自注册部分。花了一点时间想出了解决方案,但一旦找到就很简单了。可以在静态虚拟变量的初始化中插入一些代码...比如:
int CMyClass::iDummy = ClassFactory::RegisterCreatorFunction(key, CMyClass::Creator);
其中函数RegisterCreatorFunction将返回任何整数,并将创建者函数指针(也必须是静态的)添加到数组/映射中。
当将所有内容封装到类中时,可以得到类似的东西:
template>
class CClassFactory {
public:
CClassFactory() {};
~CClassFactory() {};
typedef _Base* (*CreatorFunction)(void);
typedef std::map<_Key, CreatorFunction, _Predicator> _mapFactory;
static _Key RegisterCreatorFunction(_Key idKey, CreatorFunction classCreator) {
get_mapFactory()->insert(std::pair<_Key, CreatorFunction>(idKey, classCreator));
return idKey;
}
static _Base* CreateInstance(_Key idKey) {
_mapFactory::iterator it = get_mapFactory()->find(idKey);
if (it != get_mapFactory()->end()) {
if (it->second) {
return it->second();
}
}
return NULL;
}
protected:
static _mapFactory * get_mapFactory() {
static _mapFactory m_sMapFactory;
return &m_sMapFactory;
}
};
现在,一切都准备好使用了。为此,需要:
static CSampleBase* SampleCreatorFunction() {
return new CSampleOne;
}
static int iDummyNr;
int CSampleOne::iDummyNr = CClassFactory::RegisterCreatorFunction(1, CSampleOne::SampleCreatorFunction);
CSampleBase * pBase = CClassFactory::CreateInstance(1);
如果不开始处理多线程,这个类将完美地工作。众所周知,MS
此外,这里展示的方式只允许每个键/基/谓词组合有一个类工厂实例。对于大多数实例来说,这是可以的,但可能有些情况下不是这样——那么一个新的模板参数将会有所帮助。