C++ 簡單實現 依賴注入(IOC)

因爲C++ 不支持「反射機制」, 在C++中須要實現依賴注入或控制反轉須要增長輔助程序。例如在Windows 開發程序中根據類名動態建立對象,須要在類定義中增長宏。本文主要介紹C++ Ioc的一種實現機制, 實現Ioc基本功能 「動態生成對象,屬性注入」。java

一、接口定義c++

簡單的屬性注入通常須要實現 "動態生成對象",「對象實例的屬性注入」。 所以定義接口類 IFactoryspring

// iocfactory.hapache

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();         //   返回下一個類工廠的指針, 工廠類採用鏈表方式組織。
};工具

要動態建立類,必需要有相應的工廠類,工廠類實現類的建立和屬性注入。工廠類採用鏈表方式組織。測試

二、工廠類模版ui

因爲不一樣的類須要不一樣的工廠類,能夠採用Templete類。this

// iocfactory.hspa

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方法,建立對象。

三、工廠類鏈表的入口類

 提供一個工廠類的入口類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();
};

四、提供宏定義。

因爲每一個類都須要實現函數名稱與函數指針的綁定,爲簡化程序編寫,類工廠由宏實現。定義以下宏

// 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,造成鏈表。

 

五、實現文件, 主要對工廠類鏈表的入口類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;
}

六、生成動態鏈接庫(Linux環境)

編譯 iocfactory.cpp,生成動態鏈接庫libdioc.so。 

七、進行功能測試

爲了驗證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文件

 

八、編寫主程序,在主程序中加載 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」。 其實現機制相似,只是將輔助代碼的從業務代碼中剝離出來,並自動生成。

相關文章
相關標籤/搜索