分享

C 简单实现 依赖注入(IOC)

 15所 2020-10-03

由于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”。 其实现机制类似,只是将辅助代码的从业务代码中剥离出来,并自动生成。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多