IOC容器
- Ioc容器是什么
- IoC实现细节
- IoC创建对象
- 擦除类型常用的方法
- 支持配置接口和实现的关系
- 总结
Ioc容器是什么
是为了实现某种机制,让某一对象创建不再直接依赖于外部对象创建。外界通过需求灵活的配置这种机制创建对象,这种机制称为控制反转(Inversion of Control, IoC).
我们一般通过依赖注入,将对象创建的依赖关系注入目标对象的构造函数中!比如A对象依赖于B对象的关系注入到A类对象的构造函数中。
IoC实现细节
IoC实际上具有两种能力:
- 对象的工厂能力,可以创建所有的对象,还能根据配置去创建对象。
- 可以创建依赖对象,应用不需要直接创建依赖对象,有IoC容器创建,实现控制反转。
为了实现这个功能,我们需要解决几个问题:
- 创建所有类型的对象
- 类型擦除(Any)
- 创建依赖对象
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;
}
以上虽然可以创建无参数的派生对象,但存在几个问题:
- 只能创建无参数对象
- 只能创建一种类型的对象
擦除类型常用的方法
- 多态(基类类型约束)
- 模板(缺点是容器的类型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();
测试结果如下:
这里的关键就是使用了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容器,并且通过关键字关联,随后通过配置文件可以很容易实现上面的效果。