参考原文地址: (原创)C++ IOC框架
我对文章的格式和错别字进行了调整,并补充并标注出了重要的部分。以下是正文。
正文
C++ 里面缺少一些有用的框架比如说 AOP 和 IOC 等,AOP 框架的实现在前面的博文中已介绍了,现在介绍 IOC 框架。
IOC 即控制反转,它的思想是由 IOC 容器来管理对象的生命周期、依赖关系等,从而使得应用程序的配置和依赖性规范与实际的应用程序代码分开。其中一个特点就是通过文本的配置文件进行应用程序组件间相互关系的配置,而不用重新修改并编译具体的代码。
IOC 不仅仅用来解除对象创建的耦合性,还可以使我们能通过配置去创建我们需要的对象,这种灵活性在我们应用开发过程中是非常有用的。 C# 和 Java 中有很多 IOC 框架,遗憾的是 C++ 中却鲜有 IOC 框架,本 IOC 框架填补了这个遗憾。
IOC 框架的实现原理:通过向 IOC 容器注册类型信息和一个唯一 key ,在创建时,根据类型信息和 key 从容器中创建一个实例。下面具体看实现代码:
#include <string>
#include <map>
#include <memory>
#include <functional>
using namespace std;
#include <Any>
#include <NonCopyable>
class IocContainer : NonCopyable
{
public:
IocContainer(void) {}
~IocContainer(void) {}
template <class T>
void RegisterType(string strKey)
{
typedef T *I;
std::function<I()> function = Construct<I, T>::invoke;
RegisterType(strKey, function);
}
template <class I, class T, typename... Ts>
void RegisterType(string strKey)
{
std::function<I *(Ts...)> function = Construct<I *, T, Ts...>::invoke;
RegisterType(strKey, function);
}
template <class I>
I *Resolve(string strKey)
{
if (m_creatorMap.find(strKey) == m_creatorMap.end())
return nullptr;
Any resolver = m_creatorMap[strKey];
std::function<I *()> function = resolver.AnyCast<std::function<I *()>>();
return function();
}
template <class I>
std::shared_ptr<I> ResolveShared(string strKey)
{
auto b = Resolve<I>(strKey);
return std::shared_ptr<I>(b);
}
template <class I, typename... Ts>
I *Resolve(string strKey, Ts... Args)
{
if (m_creatorMap.find(strKey) == m_creatorMap.end())
return nullptr;
Any resolver = m_creatorMap[strKey];
std::function<I *(Ts...)> function = resolver.AnyCast<std::function<I *(Ts...)>>();
return function(Args...);
}
template <class I, typename... Ts>
std::shared_ptr<I> ResolveShared(string strKey, Ts... Args)
{
auto b = Resolve<I, Ts...>(strKey, Args...);
return std::shared_ptr<I>(b);
}
private:
template <typename I, typename T, typename... Ts>
struct Construct
{
static I invoke(Ts... Args) { return I(new T(Args...)); }
};
void RegisterType(string strKey, Any constructor)
{
if (m_creatorMap.find(strKey) != m_creatorMap.end())
throw std::logic_exception("this key has already exist!");
m_creatorMap.insert(make_pair(strKey, constructor));
}
private:
unordered_map<string, Any> m_creatorMap;
};
使用说明
类型注册分成三种方式注册,一种是简单方式注册,它只需要具体类型信息和 key ,类型的构造函数中没有参数,从容器中取也只需要类型和 key ;另外一种简单注册方式需要接口类型和具体类型,返回实例时,可以通过接口类型和 key 来得到具体对象;第三种是构造函数中带参数的类型注册,需要接口类型、 key 和参数类型,获取对象时需要接口类型、 key 和参数。返回的实例可以是普通的指针也可以是智能指针。需要注意的是 key 是唯一的,如果不唯一,会产生一个断言错误,推荐用类型的名称作为 key ,可以保证唯一性,std::string strKey = typeid(T).name()。
由于用到了 C++11 的可变模板参数,所以对编译器有要求,windows 中需要 vs2012 的 Microsoft Visual C++ Compiler Nov 2012 CTP;linux GCC4.7及以上。
测试代码
struct ICar
{
virtual ~ICar() {}
virtual void test() const = 0;
};
struct Bus : ICar
{
Bus(int i, float f){};
void test() const { std::cout << "Bus::test()"; }
};
class IX
{
public:
IX() {}
virtual ~IX() {}
virtual void g() = 0;
private:
};
class X : public IX
{
public:
void g()
{
std::cout << "it is a test" << std::endl;
}
};
void TestMyIoc()
{
//简单注册,需要类型信息和key
IocContainer ioc;
ioc.RegisterType<X>("ss");
ioc.RegisterType<X>("ss"); //key重复,会报错
auto ix = ioc.ResolveShared<X>("ss");
ix->g();
//简单注册,需要类型信息、接口类型和key
ioc.SimpleRegisterType<IX, X>("ff");
auto ix1 = ioc.ResolveShared<IX>("ff");
ix1->g();
//含参数的类型注册,需要类型信息、接口类型、参数类型和key
ioc.RegisterType<ICar, Bus, int, float>("bus");
int a = 1;
float b = 2.0;
auto mycar = ioc.ResolveShared<ICar>("bus", a, b);
mycar->test();
}
测试结果
it is a test
it is a test
Bus::test()
补充一点
当 resolve 的参数类型为指针类型时,直接将 nullptr 作为参数传入会报异常,应该将 nullptr 强制转换为注册时的参数类型。
ioc.Resolve<BaseAppChain>(str, (BaseAppChain*)nullptr);