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

C++ 简单实现 依赖注入(IOC)的更多相关文章

  1. TypeC一个微软开发的超简单.NET依赖注入/IoC容器

    控制反转(IoC,Inversion of Control)是由Martin Fowler总结出来的一种设计模式,用来减少代码间的耦合.一般而言,控制反转分为依赖注入(Dependency Injec ...

  2. 从壹开始前后端分离【 .NET Core2.0 +Vue2.0 】框架之九 || 依赖注入IoC学习 + AOP界面编程初探

    更新 1.如果看不懂本文,或者比较困难,先别着急问问题,我单写了一个关于依赖注入的小Demo,可以下载看看,多思考思考注入的原理: https://github.com/anjoy8/BlogArti ...

  3. 大话DI依赖注入+IOC控制反转(二) 之 浅析.Net Core中的DI与IOC

      转发时请注明原创作者及地址,否则追究责任.原创:alunchen 在上一篇文章中,我们聊了很多关于定义的方面,比较孤燥,下面我们结合.Net Core聊一下依赖注入&控制反转. 三种对象生 ...

  4. Z从壹开始前后端分离【 .NET Core2.2/3.0 +Vue2.0 】框架之九 || 依赖注入IoC学习 + AOP界面编程初探

    本文梯子 本文3.0版本文章 更新 代码已上传Github+Gitee,文末有地址 零.今天完成的绿色部分 一.依赖注入的理解和思考 二.常见的IoC框架有哪些 1.Autofac+原生 2.三种注入 ...

  5. android使用篇(四) 注解依赖注入IOC实现绑定控件

    在android使用篇(三) MVC模式中提到一个问题: 1) 视图层(View):一般採用XML文件进行界面的描写叙述,使用的时候能够很方便的引入,可是用xml编写了,又须要在Acitvity声明而 ...

  6. 关于依赖注入IOC/DI的感想

    之前一直不明白依赖注入有什么好处,甚至觉得它是鸡肋,现在想想,当时真是可笑. 这个想法正如同说接口是没有用处一样. 当整个项目非常庞大,各个方法之间的调用非常复杂,那么,可以想象一下,假设说没有任何的 ...

  7. Spring控制反转与依赖注入(IOC、DI)

    IOC: 反转控制   Inverse Of Control DI:依赖注入 Dependency Injection 目的:完成程序的解耦合 解释:在应用系统的开发过程中,有spring负责对象的创 ...

  8. 简单解析依赖注入(控制反转)在Spring中的应用

    IoC——Inversion of Control  控制反转DI——Dependency Injection   依赖注入 大家都知道,依赖注入是Spring中非常重要的一种设计模式.可能很多初学者 ...

  9. 依赖注入 IOC

    首先,明确下IoC是什么东西: 控制反转(Inversion of Control,英文缩写为IoC)是一个重要的面向对象编程的法则来削减计算机程序的耦合问题,也是轻量级的Spring框架的核心. 控 ...

随机推荐

  1. Machine Schedule为什么UVA过了POJ过不了

    UVA1194 POJ1325 POJ要多判一个非零!!! #include<cstdio> #include<vector> #include<cstring> ...

  2. 1626:【例 2】Hankson 的趣味题

    1626:[例 2]Hankson 的趣味题题解 [题目描述] Hanks 博士是 BT(Bio-Tech,生物技术)领域的知名专家,他的儿子名叫 Hankson.现在,刚刚放学回家的 Hankson ...

  3. openpyxl -用于读/写Excel 2010 XLSX/XLSM文件的python库

    openpyxl -用于读/写Excel 2010 XLSX/XLSM文件的python库¶ https://www.osgeo.cn/openpyxl/index.html

  4. Linux之防火墙【CentOS 7】

    CentOS 7默认使用的是firewall作为防火墙,这里改为iptables防火墙. firewall操作: # service firewalld status; #查看防火墙状态 (disab ...

  5. linux中如何修改最大文件句柄数

    1.使用ulimit -a可以查看,其中的open files后面的数就是最大文件句柄数 2.临时方法:使用ulimit -n size修改最大文件句柄数(这种方法只针对当前进程有效) 3.永久方法: ...

  6. JavaWeb_(Spring框架)用户登陆Spring整合到Servlet中

    一.使用servlet技术开发用户登陆功能 在MySQL中准备一个user表,表中增加一条假数据 使用Servlet实现用户登陆的功能 用户登陆的<from>表单 <form id= ...

  7. ArcGIS超级工具SPTOOLS-拓扑错误处理

    1.1  删除线面直线上的点 操作视频: https://weibo.com/tv/v/Hxjgmuv6F?fid=1034:4379388532225679 删除面要素.线要素一条边直线上的点. 1 ...

  8. <JavaScript> 关于闭包和this对象

    1.this指向windows是如何得出的 var name = "The Window"; var object = { name : "My Object" ...

  9. Onvif协议及其在Android下的实现

    好久没有写博客,今天将前段时间做的Onvif协议在Android上的实现分享给大家. 首先,我们先来了解一下什么是Onvif协议:ONVIF 协议是由Open Network Video Interf ...

  10. 003-多线程-JUC线程池-几种特殊的ThreadPoolExecutor【newFixedThreadPool、newCachedThreadPool、newSingleThreadExecutor、newScheduledThreadPool】

    一.概述 在java doc中,并不提倡我们直接使用ThreadPoolExecutor,而是使用Executors类中提供的几个静态方法来创建线程池: 以下方法是Executors下的静态方法,Ex ...