由于C++ 不支持“反射机制”, 在C++中需要实现依赖注入或控制反转需要增加辅助程序。例如在Windows 开发程序中根据类名动态创建对象,需要在类定义中增加宏。本文主要介绍C++ Ioc的一种实现机制, 实现Ioc基本功能 “动态生成对象,属性注入”。 1、接口定义 简单的属性注入一般需要实现 '动态生成对象',“对象实例的属性注入”。 因此定义接口类 IFactory // iocfactory.h class IFactory{ public: virtual bool setIntProperty(void* obj, std::string name, int value); // 设置Int属性 virtual bool setStringProperty(void* obj, std::string name, std::string& value); // 设置string 属性 virtual bool setObjectProperty(void* obj, std::string name, void* value); // 设置指向实例对象的指针。 virtual void* createObject(); // 创建指定对象实例 virtual std::string getClassName(); // 返回对象指针 virtual IFactory* nextClass(); // 返回下一个类工厂的指针, 工厂类采用链表方式组织。 }; 要动态创建类,必须要有相应的工厂类,工厂类实现类的创建和属性注入。工厂类采用链表方式组织。 2、工厂类模版 由于不同的类需要不同的工厂类,可以采用Templete类。 // iocfactory.h template <typename T> class FactoryTemplate : public IFactory { public: std::map<std::string, void (T::*)(int)> *getIntMap() { static std::map<std::string,void (T::*)(int)> IntMap; return &IntMap; }; std::map<std::string, void (T::*)(std::string)> *getStrMap() { static std::map<std::string,void (T::*)(std::string)> StrMap; return &StrMap; }; std::map<std::string, void (T::*)(void*)> *getObjMap() { static std::map<std::string,void (T::*)(void*)> ObjMap; return &ObjMap; }; bool setIntProperty(void* obj, std::string name, int value) { typename std::map<std::string,void (T::*)(int) >::iterator iter; iter=getIntMap()->find(name); if(iter!=getIntMap()->end()){ ((T*)obj->*(iter->second))(value); return true; }else return false; } bool setStringProperty(void* obj, std::string name, std::string value) { typename std::map<std::string,void (T::*)(std::string) >::iterator iter; iter=getStrMap()->find(name); if(iter!=getStrMap()->end()){ ((T*)obj->*(iter->second))(value); return true; }else return false; } bool setObjectProperty(void* obj, std::string name, void* value) { typename std::map<std::string,void (T::*)(void*)>::iterator iter; iter=getObjMap()->find(name); if(iter!=getObjMap()->end()){ ((T*)obj->*(iter->second))(value); return true; }else return false; } void* createObject() { return new T(); } virtual std::string getClassName(){ return std::string('FactoryTemplate'); } virtual IFactory* nextClass(){ return NULL; } }; 在class FactoryTemplate中的 getIntMap(), getStrMap(), getObjMap() 使用map存储 “函数名”和“相应的函数指针”。 设置Int类型属性的函数setIntProperty(void* obj, std::string name, int value)中, obj为已创建的对象的指针,name为设置属性的方法名,value为设置值。 该函数通过在IntMap中查找对应name值的函数指针,然后使obj指向的对象执行相应的函数方法(如setXXX方法),将属性值value注入到obj中。 setStringProperty与setObjectProperty方法与setIntProperty类似。 createObject() 中执行new方法,创建对象。 3、工厂类链表的入口类 提供一个工厂类的入口类ClassFactory // iocfactory.h class ClassFactory{ public: static IFactory* FirstFactory; // 静态指针, 指向第一个工厂类。 static void* createObject(std::string className); // 根据类名创建对象 static bool setIntProperty(std::string className, void* obj, std::string propname, int value); // 根据类名,对象指针,int属性名,设置int属性值 static bool setStringProperty(std::string className, void* obj, std::string propname, std::string value); // 根据类名,对象指针,string属性名,设置string属性值 static bool setObjectProperty(std::string className, void* obj, std::string propname, void* value); // // 根据类名,对象指针,指针属性名,设置指针属性值 static IFactory** getPointer(); }; 4、提供宏定义。 由于每个类都需要实现函数名称与函数指针的绑定,为简化程序编写,类工厂由宏实现。定义如下宏 // iocfactory.h #define DECLARE_IOC(className) \ class CF_##className : public FactoryTemplate<className> { \ public: \ IFactory* NextFactory; \ std::string ClassName; \ std::string getClassName() ; \ IFactory* nextClass(); \ void ListBuild(); \ CF_##className(); }; #define IMPLEMENT_IOC_START(className) \ std::string CF_##className::getClassName(){ return ClassName; }; \ IFactory* CF_##className::nextClass(){ return NextFactory; }; \ void CF_##className::ListBuild(){ NextFactory=ClassFactory::FirstFactory; ClassFactory::FirstFactory=this; } \ CF_##className::CF_##className (): ClassName(#className) { ListBuild(); \ #define IMPLEMENT_IOC_BIND_INT(className, propName, funName) \ getIntMap()->insert(std::pair<std::string, void (className::*)(int)>(#propName, &className::funName )); #define IMPLEMENT_IOC_BIND_STR(className, propName, funName) \ getStrMap()->insert(std::pair<std::string, void (className::*)(std::string)>(#propName, &className::funName)); #define IMPLEMENT_IOC_BIND_OBJ(className, propName, funName) \ getObjMap()->insert(std::pair<std::string, void (className::*)(void*)>(#propName, reinterpret_cast< void (className::*)(void*) >( &className::funName ))); #define IMPLEMENT_IOC_END(className) \ }; \ static class CF_##className _init_##className; DECLARE_IOC 宏用来定义工厂类, IMPLEMENT_IOC_START , IMPLEMENT_IOC_BIND_INT,IMPLEMENT_IOC_BIND_STR,IMPLEMENT_IOC_BIND_OBJ, IMPLEMENT_IOC_END 工厂类在构造方法中,通过IMPLEMENT_IOC_BIND_INT,IMPLEMENT_IOC_BIND_STR,IMPLEMENT_IOC_BIND_OBJ 将名称与函数指针关联起来。 BIND宏必须在IMPLEMENT_IOC_START 与 IMPLEMENT_IOC_END 宏之间。每个注入属性都需要添加相应的BIND宏。最后添加一个static对象_init_##className,形成链表。 5、实现文件, 主要对工厂类链表的入口类ClassFactory类的静态方法进行编写, ClassFactory通过查找工厂类链表,找到对应的执行函数,并执行; // iocfactory.cpp #include 'iocfactory.h' IFactory* ClassFactory::FirstFactory=NULL; bool IFactory::setIntProperty(void* obj, std::string name, int value) {return false;}; bool IFactory::setStringProperty(void* obj, std::string name, std::string& value) { return false;}; bool IFactory::setObjectProperty(void* obj, std::string name, void* value) { return false;}; void* IFactory::createObject() { return NULL;}; std::string IFactory::getClassName(){ return std::string('IFactory'); } IFactory* IFactory::nextClass(){ return NULL; } void* ClassFactory::createObject(std::string className) { IFactory* pfactory; for(pfactory=ClassFactory::FirstFactory; pfactory!=NULL; pfactory=pfactory->nextClass()) { if(className==pfactory->getClassName()) return pfactory->createObject(); } return NULL; };
bool ClassFactory::setIntProperty(std::string className, void* obj, std::string propname, int value) { IFactory* pfactory; for(pfactory=ClassFactory::FirstFactory; pfactory!=NULL; pfactory=pfactory->nextClass()) { if(className==pfactory->getClassName()) return pfactory->setIntProperty(obj, propname, value); } return false; } bool ClassFactory::setStringProperty(std::string className, void* obj, std::string propname, std::string value) { IFactory* pfactory; for(pfactory=ClassFactory::FirstFactory; pfactory!=NULL; pfactory=pfactory->nextClass()) { if(className==pfactory->getClassName()) return pfactory->setStringProperty(obj, propname, value); } return false; } bool ClassFactory::setObjectProperty(std::string className, void* obj, std::string propname, void* value) { IFactory* pfactory; for(pfactory=ClassFactory::FirstFactory; pfactory!=NULL; pfactory=pfactory->nextClass()) { if(className==pfactory->getClassName()) return pfactory->setObjectProperty(obj, propname, value); } return false; } IFactory** ClassFactory::getPointer() { return &FirstFactory; } 6、生成动态连接库(Linux环境) 编译 iocfactory.cpp,生成动态连接库libdioc.so。 7、进行功能测试 为了验证ClassFactory类是否能实现“动态生成对象”和“属性注入”, 编写两个简单的测试类base, base2 代码如下 // base.h class base{ public: int x; int y; void setX(int x); void setY(int y); base(); }; DECLARE_IOC(base) //base.cpp #include 'base.h' void base::setX(int xx){ std::cout<<'this is BASE setX()\n'; x=xx; }; void base::setY(int yy){ std::cout<<'BASE setY \n'; y=yy; }; base::base() { std::cout<<'basse contructor \n' ; }; IMPLEMENT_IOC_START(base) IMPLEMENT_IOC_BIND_INT(base, x, setX) IMPLEMENT_IOC_BIND_INT(base, y, setY) IMPLEMENT_IOC_END(base) 上面是base类的头文件和cpp文件,以下是base2.cpp类的头文件和cpp文件 //base2.h #include 'iocfactory.h' class base2{ public: int x; int y; void setX(int x); void setY(int y); base2(); }; DECLARE_IOC(base2) // base2.cpp #include 'base2.h' void base2::setX(int xx){ x=xx; std::cout<<'this is base2 setxxxxxxxxxx\n'; }; void base2::setY(int yy){ std::cout<<'this is base2 setY\n'; y=yy; }; base2::base2() { std::cout<<'base2 contructor \n'; }; IMPLEMENT_IOC_START(base2) IMPLEMENT_IOC_BIND_INT(base2, x, setX) IMPLEMENT_IOC_BIND_INT(base2, y, setY) IMPLEMENT_IOC_END(base2) 将base.cpp 和base2.cpp 分别编译为动态so文件 8、编写主程序,在主程序中加载 iocfactory, base, base2相关的so文件, 程序运行时,动态库文件需要在环境变量LD_LIBRARY_PATH目录下。 #include 'base.h' #include 'base2.h' #include 'iocfactory.h' #include <dlfcn.h> int main() { void* pHandle, *pHandle1; pHandle=dlopen('libdbase2.so', RTLD_NOW); if(!pHandle) { std::cout<<'Can't load libdioc.so\n'; } pHandle1=dlopen('libdbase.so',RTLD_NOW); if(!pHandle1) { std::cout<<'cant' load libdbase.so\n'; } //std::cout<<dlerror(); //std::cout<<'before createObject\n'; base* pbase=(base*)ClassFactory::createObject('base'); base2* pbase2=(base2*)ClassFactory::createObject('base2'); ClassFactory::setIntProperty('base', pbase, 'x', 10000); std::cout<<pbase->x<<std::endl; ClassFactory::setIntProperty('base2', pbase2, 'y', 23300); std::cout<<pbase2->y<<std::endl; return 0; } 程序输出: basse contructor base2 contructor this is BASE setX() 10000 this is base2 setY 23300 根据程序输出结果看, 在main函数中,通过类名实现了动态创建对象,设置属性。 即base类和base2类的创建和设置属性的方法全部委托给ClassFactory实现,从而实现依赖注入。进一步,通过动态库和多态机制,可以实现通过配置文件来装载不同的实现类,类似spring通过配置文件实现的依赖注入功能。 上面的代码的缺点是,需要在base,base2中增加相应的宏才能实现我们想要的功能。目前c++是无法实现java的反射机制,因此要实现“动态类生成,属性注入”等功能,必然需要增加代码。 apache有个c++ ioc项目,实现了不在源代码中添加程序,但是需要通过工具扫描源文件,生成相应的辅助代码,通过辅助代码实现“Ioc”。 其实现机制类似,只是将辅助代码的从业务代码中剥离出来,并自动生成。
|