最近在做一些项目的时候,经常需要用到类的动态注册和获取,使得调用者可以根据名字创建一个类的实例,然后调用改类的虚函数完成功能调用,类似于建立一套数据库访问套件,可以做到后期增加何种数据库访问的无需修改原有代码,需要做的仅仅是按照一定的规范创建一个新类,该类会完成自身的注册,调用者根据名字获取改类实例即可,涉及到的技术包括模板、singletons、object factory方,下面将涉及到的类列在下面并进行一些描述:
// ClassRegister.h: interface for the CClassRegister class.
//
//////////////////////////////////////////////////////////////////////
#if !defined(AFX_CLASSREGISTER_H__809D89C4_E53F_43CE_8DE4_37248A855CCB__INCLUDED_)
#define AFX_CLASSREGISTER_H__809D89C4_E53F_43CE_8DE4_37248A855CCB__INCLUDED_
#ifdef _WIN32
// 这里假设windows平台上使用的编译器是VC,由于VC自身携带的STL实现会导致
// 使用map时出现大量的警告,因此关闭这个警告
#pragma warning (disable:4786)
#endif
// 引入必要的头文件
#include <map>
#include <string>
#include <assert.h>
namespace michael
{
/**
* 当找不到指定的对象时的缺省处理方法,缺省方法就是抛出一个异常
*/
template<class _IdentifierType, class _ClassType>
class CDefaultFactoryError
{
public:
/**
* 从基本的异常类继承,用以实现无法找到对象时的异常抛出
*/
class Exception : public std::exception
{
public:
Exception(const _IdentifierType &unknownId)
: m_unknownId(unknownId)
{
}
virtual const char *what() const
{
return "Unknown Object type passed to Factory.";
}
const _IdentifierType &getId()
{
return m_unknownId;
}
private:
_IdentifierType m_unknownId;
};
protected:
virtual _ClassType *OnUnknownType(const _IdentifierType &id) const
{
throw Exception(id);
}
};
/**
* 这里提供了另外一种常见的处理方法,即找不到对象时返回NULL,
* 可以通过模板参数来替换,但是下面的注册和创建宏则无法使用
* 需要自己写代码或者宏
*/
template<class _IdentifierType, class _ClassType>
class CNullFactoryError : public CDefaultFactoryError<_IdentifierType, _ClassType>
{
protected:
virtual _ClassType *OnUnknownType(const _IdentifierType &id) const
{
return NULL;
}
};
/**
* 类工厂的实现类
* 有四个模板参数
* 第一个模板参数是抽象类
* 第二个模板参数是标识符的类型,这里采用字符串,也就是说用名字可以获取类实例
* 第三个模板参数是创建实际对象的时候调用的方法或者访函式
* 第四个模板参数是类工厂的基类,用于处理根据名字无法创建对象时的处理方法
*/
template
<
class _AbstractClass,
class _IdentifierType = std::string,
class _ProductCreator = _AbstractClass* (*)(),
class _FactoryErrorPolicy = CDefaultFactoryError<_IdentifierType, _AbstractClass>
>
class CClassFactory : _FactoryErrorPolicy
{
public:
/**
* 用于对类进行注册
*/
bool Register(const _IdentifierType &id, _ProductCreator creator)
{
return m_associations.insert(AssocMap::value_type(id, creator)).second;
}
/**
* 取消注册
*/
bool Unregister(const _IdentifierType &id)
{
return m_associations.erase(id);
}
/**
* 根据标识符创建对象实例,并已抽象类指针的方式返回
*/
_AbstractClass *CreateObject(const _IdentifierType &id) const
{
AssocMap::const_iterator it = m_associations.find(id);
if ( m_associations.end() != it )
{
return (it->second)();
}
return OnUnknownType(id);
}
/**
* 类工厂对象的实例
*/
static CClassFactory *Instance()
{
static CClassFactory<_AbstractClass, _IdentifierType,
_ProductCreator, _FactoryErrorPolicy> inst;
return &inst;
}
public:
typedef std::map<_IdentifierType, _ProductCreator> AssocMap;
AssocMap m_associations;
CClassFactory(){}
CClassFactory(const CClassFactory &other){}
virtual ~CClassFactory(){}
CClassFactory &operator=(const CClassFactory &other){}
};
#ifndef DYNAMIC_REGISTER_CLASS
/**
* 这里尝试用一个仿函式来替代下面宏生成的createObject但是还没有解决
* 多模板类之间数据共享的问题
*/
template <class T>
class SimpleCreater
{
public:
T *operator()() const
{
return new T();
}
};
/**
* 定义了一个宏,将这个宏签入到具体实现的类中,为类增加一个静态函数
* 该函数具体创建对象的实例,两个模板参数分别时真实的动态注册类和
* 抽象类
* 该宏需要签入到类的public部分
*/
#define DYNAMIC_CREATE_CLASS(cls, abstract) static abstract *createObject() { return new cls(); }
/**
* 定义一个宏自动完成类的注册
* 该宏需要插入在类的实现文件中
*/
#define SIMPLY_DYNAMIC_REGISTER_CLASS(abstract, cls, strname) const bool bRet = michael::CClassFactory<abstract>::Instance()-> Register(strname, cls::createObject);
/**
* 定义一个宏可以根据名字获取一个对象的实例
*/
#define SIMPLY_CREATE_OBJECT(abstract, strname) michael::CClassFactory<abstract>::Instance()-> CreateObject(strname);
/************************************************************************/
/* 下面的两个宏根上面的功能相同只不过他们采用了另外一种错误处理方式 */
/* 当无法招到对象时返回NULL,而不是抛出异常
/************************************************************************/
/**
* 定义一个宏自动完成类的注册
* 该宏需要插入在类的实现文件中
*/
#define SIMPLY_DYNAMIC_REGISTER_CLASS_NULL(abstract, cls, strname) const bool bRet = michael::CClassFactory< abstract, std::string, abstract* (*)(), michael::CNullFactoryError<std::string, abstract> >::Instance()-> Register(strname, cls::createObject);
/**
* 定义一个宏可以根据名字获取一个对象的实例
*/
#define SIMPLY_CREATE_OBJECT_NULL(abstract, strname) michael::CClassFactory< abstract, std::string, abstract* (*)(), michael::CNullFactoryError<std::string, abstract> >::Instance()-> CreateObject(strname);
#endif
}
#endif // !defined(AFX_CLASSREGISTER_H__809D89C4_E53F_43CE_8DE4_37248A855CCB__INCLUDED_)
下面结合例子说明一下如何使用:
下面声明一个抽象类他定义了一些方法但是没有任何实现,下面的例子非常简单无法进行真正的应用,目的是说明上面的动态注册方法
// 头文件 Database.h
class CDatabase
{
public:
virtual void open() = 0;
virtual void close() = 0;
};
// 头文件 OracleDB.h
class COracleDB : public CDatabase
{
public:
// 这里说明了COracleDB类需要动态注册,并在内部实现了createObject函数
DYNAMIC_CREATE_CLASS(COracleDB , CDatabase)
virtual void open();
virtual void close();
}
// 实现文件COracleDB.cpp
#include "OracleDB.h"
// 这个宏完整了真正的注册
SIMPLY_DYNAMIC_REGISTER_CLASS(CDatabase, COracleDB, "oracle")
void COracleDB::open()
{
// 一些实现
}
void COracleDB::close()
{
// 一些实现
}
上面的代码完成后,真正的调用者就可以根据字符串获取类的实例了
int main()
{
CDatabase *pDb = SIMPLY_CREATE_OBJECT(CDatabase, "oracle");
pDb->open();
pDb->close();
return 0;
}
当后期需要增加对mysql支持的时候可以按照上面oracle的方法实现一个mysql的类,然后在主函数里面更换一下类名即刻,无需任何实质的改动,更可以通过配置文件或者主函数的传入参数来说明数据库类别,距离如下:
int main(int argc, char **argv)
{
if ( argc != 2 )
{
return -1;
}
CDatabase *pDb = SIMPLY_CREATE_OBJECT(CDatabase, argv[1]);
pDb->open();
pDb->close();
return 0;
}
如上即实现了动态注册和获取,并且这部分代码是完全C++标准的,不依赖于MFC等类库,在跨平台应用中可以起到很好的作用,因为涉及到的理论很多,而且都比较庞大,这里就没有介绍原理,而仅仅是说明如何使用,感兴趣的朋友可以自己看一下关于模板、singletons、object factory方面的知识。:)
希望能够给大家一些启事,环境交流,呵呵。