【IoC容器】C++实现

版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/gx864102252/article/details/82193904

本系列文档从属于:C++11应用实践系列

部分Demo和结论引用自<深入应用C++11代码优化与工程>这本书

Ioc容器是什么

是为了实现某种机制,让某一对象创建不再直接依赖于外部对象创建。外界通过需求灵活的配置这种机制创建对象,这种机制称为控制反转(Inversion of Control, IoC).
我们一般通过依赖注入,将对象创建的依赖关系注入目标对象的构造函数中!比如A对象依赖于B对象的关系注入到A类对象的构造函数中。

IoC实现细节

IoC实际上具有两种能力:

  1. 对象的工厂能力,可以创建所有的对象,还能根据配置去创建对象。
  2. 可以创建依赖对象,应用不需要直接创建依赖对象,有IoC容器创建,实现控制反转。

为了实现这个功能,我们需要解决几个问题:

  1. 创建所有类型的对象
  2. 类型擦除(Any)
  3. 创建依赖对象

    IoC创建对象

#include <iostream>
#include <string>
#include <map>
#include <functional>
#include <memory>
using namespace std;

template<class T>
class IocContainer
{
public:
    using FuncType = std::function<T*()>;

    //注册一个key对应的类型
    template<class ClassType>
    void registerType(string key)
    {
        FuncType func = [] {return new ClassType(); };
        registerType(key, func);
    }

    //根据唯一标志查询对应的构造器 创建对象
    T* resolve(string key)
    {
        if (m_map.find(key) == m_map.end())
        {
            return nullptr;
        }
        auto func = m_map[key];
        return func();
    }
    std::shared_ptr<T> resolveShared(string key)
    {
        if (m_map.find(key) == m_map.end())
        {
            return nullptr;
        }
        auto func = m_map[key];
        return std::shared_ptr<T>(func());
    }

private:
    void registerType(string key, FuncType type)
    {
        if (m_map.find(key) != m_map.end())
        {
            throw std::invalid_argument("this key has exist");
        }
        m_map.emplace(key, type);
    }

private:
    std::map<string, FuncType> m_map;
};

struct ICar
{
    virtual ~ICar() {}
    virtual void test() const = 0;
};

struct Car : ICar
{
    void test() const
    {
        cout << "Car test" << endl;
    }
};

struct Bus : ICar
{
    void test() const
    {
        cout << "Bus test" << endl;
    }
};

int main()
{
    IocContainer<ICar> ioc;
    ioc.registerType<Bus>("bus");
    ioc.registerType<Car>("car");

    std::shared_ptr<ICar> bus = ioc.resolveShared("bus");
    bus->test();                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             

    return 0;
}

以上虽然可以创建无参数的派生对象,但存在几个问题:

  1. 只能创建无参数对象
  2. 只能创建一种类型的对象

擦除类型常用的方法

  • 多态(基类类型约束)
  • 模板(缺点是容器的类型T必须要指定)
  • 类型容器(Variant可以承载指定的某些类型)
  • 通用类型擦除(Any类型可以满足使用)
  • 闭包 (例如使用lambda表达式封装起来)
    本次实现的Ioc容器主要使用Any和闭包擦除技巧实现。

Any和闭包擦除类型

上面讲的Ioc容器必须要知道容器存储的数据类型,我们通过一个Any类型擦除这个限制。Any实现见:链接

class IocContainer
{
public:

    //注册一个key对应的类型
    template<class T, class Depend, class =
        std::enable_if<!std::is_same<Depend, void>::value>::type>
    void registerType(string key)
    {
        std::function<T*()> func = [] {return new T(new Depend()); };
        registerType(key, func);
    }

    //简单的对象工厂
    template<class T>
        void registerType(string key)
    {
        std::function<T*()> func = [] {return new T(); };
        registerType(key, func);
    }

    //根据唯一标志查询对应的构造器 创建对象
    template<class T>
    T* resolve(string key)
    {
        if (m_map.find(key) == m_map.end())
        {
            return nullptr;
        }
        Any func = m_map[key];

        //转换为合适的类型
        std::function<T*()> f = func.cast<std::function<T*()>>();
        return f();
    }

    template<class T>
    std::shared_ptr<T> resolveShared(string key)
    {
        T* t = resolve<T>(key);
        return std::shared_ptr<T>(t);
    }

private:
    void registerType(string key, Any func)
    {
        if (m_map.find(key) != m_map.end())
        {
            throw std::invalid_argument("this key has exist");
        }
        m_map.emplace(key, func);
    }

private:
    std::map<string, Any> m_map;
};

测试程序如下:


struct ICar
{
    virtual ~ICar() {}
    virtual void test() const {}
};

struct Car 
{
    void test() const
    {
        cout << "Car test" << endl;
    }
};

struct Bus
{
    void test() const
    {
        cout << "Bus test" << endl;
    }
};

struct Base
{
    virtual void func() {}
    virtual ~Base(){}
};

struct DerivedB : Base
{
    virtual void func() {
        cout << "derived b" << endl;
    }
};

struct DerivedA : Base
{
    virtual void func() {
        cout << "derived a" << endl;
    }
};

struct A
{
    A(Base* ptr) : m_ptr(ptr) {}

    virtual void func() {
        m_ptr->func();
    }

private:
    Base* m_ptr;
};


    IocContainer ioc;
    ioc.registerType<A, DerivedA>("A");
    ioc.registerType<A, DerivedB>("B");

    auto pa = ioc.resolveShared<A>("A");
    auto pb = ioc.resolveShared<A>("B");

    pa->func();
    pb->func();
    cout << "-------------------" << endl;

    ioc.registerType<Bus>("bus");
    ioc.registerType<Car>("car");
    auto pbus = ioc.resolveShared<Bus>("bus");
    auto pcar = ioc.resolveShared<Car>("car");
    pbus->test();
    pcar->test();

测试结果如下:

这里写图片描述

扫描二维码关注公众号,回复: 3195524 查看本文章

这里的关键就是使用了lambda表达式擦除具体类型,然后将其赋值给Any类型。

IoC支持可变参数

上面实现的IoC容器,依赖的对象构造函数都是无参数的,下面我们需要增加入参测试。

class IocContainer
{
public:

    //注册一个key对应的类型
    template<class T, class Depend, typename ...Args>
        void registerType(string key)
    {
        std::function<T*(Args...)> func = [] (Args...args){return new T(new Depend(args...)); };
        registerType(key, func);
    }

    //根据唯一标志查询对应的构造器 创建对象
    template<class T, typename...Args>
    T* resolve(string key, Args...args)
    {
        if (m_map.find(key) == m_map.end())
        {
            return nullptr;
        }
        Any func = m_map[key];

        //转换为合适的类型
        std::function<T*(Args...)> f = func.cast<std::function<T*(Args...)>>();
        return f(args...);
    }

    template<class T, typename...Args>
    std::shared_ptr<T> resolveShared(string key, Args...args)
    {
        T* t = resolve<T>(key, args...);
        return std::shared_ptr<T>(t);
    }

private:
    void registerType(string key, Any func)
    {
        if (m_map.find(key) != m_map.end())
        {
            throw std::invalid_argument("this key has exist");
        }
        m_map.emplace(key, func);
    }

private:
    std::map<string, Any> m_map;
};

struct Base
{
    virtual void func() {}
    virtual ~Base() {}
};

struct Derived : Base
{
    Derived(int a, double d) :m_a(a), m_d(d) {}
    virtual void func() {
        cout << "derived :"<<m_a + m_d << endl;
    }
private:
    int m_a;
    double m_d;
};

struct A
{
    A(Base* ptr) : m_ptr(ptr) {}

    virtual void func() {
        m_ptr->func();
    }

private:
    Base* m_ptr;
};

//测试代码
    IocContainer ioc;
    ioc.registerType<A, Derived, int, double>("A");
    auto pa = ioc.resolveShared<A>("A", 1, 2.0);
    pa->func();

这里写图片描述

支持配置接口和实现的关系

我们需要支持将接口和派生类的关系进行配置,这样后面就可以根据参数配置接口和派生类的关系。
主要修改的点就是判断继承关系,如果是派生类则直接创建对象。

    //T和Depend没有继承关系
    template<class T, class Depend, typename ...Args>
    typename std::enable_if<!std::is_base_of<T, Depend>::value>::type //此时type为void
    registerType(string key)
    {
        std::function<T*(Args...)> func = [] (Args...args){return new T(new Depend(args...)); };
        registerType(key, func);
    }
    //T和Depend有继承关系
    template<class T, class Depend, typename ...Args>
    typename std::enable_if<std::is_base_of<T, Depend>::value>::type //此时type为void
        registerType(string key)
    {
        std::function<T*(Args...)> func = [](Args...args) {return new Depend(args...); };
        registerType(key, func);
    }
struct Interface
{
    virtual void func() {}
    virtual ~Interface() {}
};

struct Derived : Interface
{
    Derived(int a, double d) :m_a(a), m_d(d) {}
    virtual void func() {
        cout << "derived :"<<m_a + m_d << endl;
    }
private:
    int m_a;
    double m_d;
};

//测试代码
    IocContainer ioc;
    ioc.registerType<Interface, Derived, int, double>("interface");
    auto pa = ioc.resolveShared<Interface>("interface", 1, 2.0);
    pa->func();

总结

通过依赖注入的方式实现控制反转,所谓依赖注入,即组件之间的依赖关系由容器在运行期决定。
由容器动态的将某种依赖关系注入到组件中,方式有很多种:C#和Java中支持构造函数、属性和方法的注入。由于C++不支持反射和标签,不能实现属性和方法的调用和注入,只能实现构造函数的依赖注入。

我们上面对象成功注册到对了Ioc容器,并且通过关键字关联,随后通过配置文件可以很容易实现上面的效果。

猜你喜欢

转载自blog.csdn.net/gx864102252/article/details/82193904