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


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

  • 相关阅读:
    Mac基本操作记录
    flutter vscode+第三方安卓模拟器
    git一些基本操作
    获取点击元素的绝对位置
    windows下提交前检测操作
    VGG网络学习
    MySQL数据库中文乱码
    配深度学习环境中的小tips(tensorflow+anaconda+keras+cuda)
    Docker安装指定的anaconda
    yolov1阅读
  • 原文地址:https://www.cnblogs.com/piepie/p/3663002.html
Copyright © 2020-2023  润新知