15所 / 待分类 / 【IoC容器】C 实现

分享

   

【IoC容器】C 实现

2020-10-03  15所

本系列文档从属于: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
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89

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

  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;};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56

测试程序如下:

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();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74

测试结果如下:

这里写图片描述

这里的关键就是使用了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();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83

这里写图片描述

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

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

//T和Depend没有继承关系template<class T, class Depend, typename ...Args>typename std::enable_if<!std::is_base_of<T, Depend>::value>::type //此时type为voidregisterType(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为voidregisterType(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();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

总结

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

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

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。如发现有害或侵权内容,请点击这里 或 拨打24小时举报电话:4000070609 与我们联系。

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多
    喜欢该文的人也喜欢 更多

    ×
    ×

    ¥.00

    微信或支付宝扫码支付:

    开通即同意《个图VIP服务协议》

    全部>>