动态订阅机制的类工厂实现

在软件开发过程中,经常需要根据不同的条件创建不同类型的对象。例如,在解析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; } };

现在,一切都准备好使用了。为此,需要:

  • 决定使用什么作为键(例如,整数/字符串/GUID/值对)并定义相应的谓词函数。
  • 在类定义中添加静态“创建者”函数和虚拟变量:
static CSampleBase* SampleCreatorFunction() { return new CSampleOne; } static int iDummyNr;
  • 要初始化虚拟变量,请调用注册创建者函数的代码:
int CSampleOne::iDummyNr = CClassFactory::RegisterCreatorFunction(1, CSampleOne::SampleCreatorFunction);
  • 要创建一个对象,只需调用:
CSampleBase * pBase = CClassFactory::CreateInstance(1);

已知限制

如果不开始处理多线程,这个类将完美地工作。众所周知,MS 本身在多线程环境中并不友好,所以这将是第一个需要注意的地方...解决方案是通过临界区保护所有类调用,但它不是一个通用解决方案,所以这里不会包含它。

此外,这里展示的方式只允许每个键/基/谓词组合有一个类工厂实例。对于大多数实例来说,这是可以的,但可能有些情况下不是这样——那么一个新的模板参数将会有所帮助。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485