Skip to content
jijinlong edited this page Nov 14, 2016 · 15 revisions

##C++依赖注入实现 我们的目标就是 能够影藏具体的实现,并且可以抽选相关的实现,使用时只约定接口即可。

   // 定义的接口
   IAction action; 
   // 选择相关的实现,在配置文件中通过ActionImp 抽选相关的实现为action赋值
   action.imp("ActionImp");    
   //注意这里的-> 他是会调用的具体实现的方法
   action->doSometing(); 

怎么能做到这点呢?我们一步步来,先实现基础的Interface类

template<typename T>
class Interface {
public:
    // 重载-> 调用子类相关函数
    T * operator->() 
    {
        if (_buffer == 0) return 0;
        return static_cast<T*>((void*)_buffer);
    }
    Interface()
    {
        _buffer = 0;
    }
    virtual ~Interface()
    {
        // 注意并不是直接delete _buffer ,若是将无法调用实现的析构
        if (_buffer) delete static_cast<T*>((void*)_buffer); _buffer = 0;
    }
protected:
    void* _buffer; // 具体实现类 所使用 的空间
};

为什么不用 纯虚函数呢? 对象在使用时必须有实现,不然C++无法分配内存,所有必须是个包含空实现的虚函数来做,我们是需要在运行期指定实现的。我们定义IAction 来继承Interface 类,Interface类帮我们实现了->重载 可以让C++为我们自动调用实现类的方法。

class IAction:public Interface<IAction> {
public:
    virtual void doSometing(){}; // 空实现 必须是虚函数
    virtual ~IAction(){}; // 务必加上
    void imp(const char *name); // 指定使用实现的类
};

我们来定义一个实现

class ActionImp:public IAction {
public:
    void doSometing(){
        printf("dosometing"); // 就输出字符串
    }
};

我们怎么将 IAction 与 ActionImp 对应呢?简单的方式,为IAction 提供一个方法,在这个方法里 指定具体的实现

void IAction::imp(const char *name)
{
     if (name == "ActionImp")
     {
         _buffer = new ActionImp(); // 根据字符串来产生不同的实现
     }
}

这也是一种方法,很LOW,如果你觉得够用就这样用,但是我们希望代码少做这些 If Else 写一个基础的工厂来生产这些对象

void IAction::imp(const char *name)
{
     _buffer = ObjectFactory::instance()->create(name); // 这样是不是更好? 
}

如何实现这样的ObjectFactory 呢?

最简单的办法 就是 ObjectFactory 根据名字分发生产对象,就是if else 的方式,我们这里通过hashmap 存放相关实现,通过名字抽选出具体的产出类,然后clone 生成当前对象

class Implement{
public:
    // 容器中存放相关不同实现的对象
    virtual void *clone(){return 0;};
}
template<typename T>
class Implement : public Implement{
public:
    T *clone()
    {
        return new T();
    }    
}; 

// ActionImp 需要 继承ImplementCreator 
// 其实就是为了你少写个 clone() 函数 你 继承Implement 实现 clone 函数也可以 
class ActionImp:public IAction,public ImplementCreator<ActiomImp> {
// 具体的实现
};

class ObjectFactory : public Singleton<ObjectFactory>{
public:
    Implement* create(const char *name)
    {
         auto it = imps.find(name);
         if (it != imps.end())
         {
              return it->second->clone();
         }
         return 0;
    }
    //初始化的时候放入具体的实现
    void init()
    {
         _imps["ActionImp"] = new ActionImp();
    }
private:
    std::unordered_map<std::string,Implement*> _imps;
}

上面的Singleton的代码如下

template<typename T>
class Singleton {
public:
    static T* instance()
    {
         return &o;
    }
private:
    static T o;
}
template<typename T>
T Singleton<T>::o;

以上就是factory 的代码实现了名字与实现的绑定,但是我们如何从配置文件中指定具体的实现呢?我们需要将ObjectFactory::init里实现的注册方式,通过配置去指定,也是我们为什么没用if-else 来产出对象的原因,如果你项目不需要XML去管理对象的产生也行,单我们这里通过XML来加载,一个名字可以对应其他的实现,ActionImp 可能对应的是 ActionImp_0的实现,引入了别名机制

class ObjectFactory : public Singleton<ObjectFactory>{
public:    
    void init()
    {
        _imps["ActionImp_0"] = new ActionImp();
        _imps["ActionImp_1"] = new ActionImp();

        XmlObjectFactory::createAllCreator(); // 通过XmlObjectFactory 来指定当前名字 所对应的真正的Implements    
   }
   Implement* create(const char *name)
    {
         auto it = _alias_imps.find(name);
         if (it != _alias_imps.end())
         {
              auto real_it = _imps.find(it->second);
              if (real_it != _imps.end())
              {
                   return real_it->second->clone();
              } 
         } 
         return 0;
    }

private:
    static std::unordered_map<std::string,Implement*> _imps; // 当前存储
    std::unordered_map<std::string,std::string> _alias_imps; // 当前别名 
};
std::unordered_map<std::string,Implement*> ObjectFactory::imps;

配置

   <config> 
     <bean name="ActionImp" class="ActionImp_0"/> <!--当前指定 ActiomImp 的实现为ActionImp_0-->
  </config>

解析配置构建 ActiomImp 与 ActiomImp_0 等得键值对即可,这部分你可以查看代码. 每次扩展实现的时候都需要在Init里填写代码,也是蛮费时的,我们需要外部注册。利用全局对象来外部构建

template<typename T>
class AutoRegisterCreator{
public:
    AutoRegisterCreator(const char *name)
    {
        ObjectFactory::imps[name] = new T();
    }
};

#define IMP(name,CLASS) AutoRegisterCreator<T> CLASS#TT(name) 

会在ObjectFactory::instance()->init 之前调用这个构造函数 从而完成自动外部注册,不用再init里加注册代码了

这里就基本完成的csspring的框架.那么csspring的框架将如何使用呢?

// 初始化ccspring
  ccs::ObjectFactory::instance()->init();
// 需要定义接口
  class YourInterface : public Interface<YourInterface> {
  public:
      virtual void doAction(){};
  };
// 约定实现的字符串
  YourInterface logic;
  logic.imp("YourImp");
// 定义实现
  class YourImplement:pulic Implement<YourImplement> {
  public:
      void doAction(){}
  };
   IMP("YourImp_0",YourImplement); // 指定当前实现的名字   
// 使用接口
  YourInterface your;
  your->doAction(); // 调用实现的具体方法
// 定义配置
  <config><bean name="YourImp" class="YourImp_0"/></config>

为啥如此复杂的使用和创建对象呢? 为的使项目的去依赖,如果你觉得需要 可以试试。

Clone this wiki locally