在软件开发中,控制反转(Inversion of Control,简称IoC)是一种设计原则,它可以用来减少程序中各个组件之间的耦合度。依赖注入(Dependency Injection,简称DI)是实现IoC的一种方式,它允许对象在创建时由外部提供其依赖项,而不是自己创建。本文将介绍如何实现一个简单的IoC容器,该容器可以管理对象的依赖关系,并在需要时提供对象实例。
在存储一个类实现特定接口的方式之前,需要为这个类分配一个类型代码。在IOC容器实现中,使用了一个静态整数变量来指定下一个将被分配的类型ID,以及一个静态局部变量实例,可以通过调用GetTypeID方法来访问。
class IOCContainer {
static int s_nextTypeId;
public:
template
static int GetTypeID() {
static int typeId = s_nextTypeId++;
return typeId;
}
};
有了类型ID之后,可以存储一个工厂对象来表示不知道如何创建对象的事实。为了在同一个集合中存储所有的工厂,选择了一个抽象基类,工厂将从这个基类派生,并实现一个捕获调用的函数。
class FactoryRoot {
public:
virtual ~FactoryRoot() {}
};
std::map> m_factories;
template
class CFactory : public FactoryRoot {
std::function()> m_functor;
public:
~CFactory() {}
CFactory(std::function()> functor) : m_functor(functor) {}
std::shared_ptr GetObject() {
return m_functor();
}
};
template
std::shared_ptr GetObject() {
auto typeId = GetTypeID();
auto factoryBase = m_factories[typeId];
auto factory = std::static_pointer_cast>(factoryBase);
return factory->GetObject();
}
现在需要填充集合。实现了几种不同的方式:可能想要显式地提供一个函数对象,或者可能想要选择一个单一实例,或者按需创建新实例。
// 最基本的实现 - 注册一个函数对象
template
void RegisterFunctor(std::function(std::shared_ptr... ts)> functor) {
m_factories[GetTypeID()] = std::make_shared>([=]{
return functor(GetObject()...);
});
}
// 注册一个对象的实例
template
void RegisterInstance(std::shared_ptr t) {
m_factories[GetTypeID()] = std::make_shared>([=]{
return t;
});
}
// 提供一个函数指针
template
void RegisterFunctor(std::shared_ptr(*functor)(std::shared_ptr... ts)) {
RegisterFunctor(std::function(std::shared_ptr... ts)>(functor));
}
// 一个工厂,将按需调用构造函数
template
void RegisterFactory() {
RegisterFunctor(std::function(std::shared_ptr... ts)>([](std::shared_ptr... arguments) -> std::shared_ptr {
return std::make_shared(std::forward>(arguments)...);
}));
}
// 一个工厂,将为每个请求返回一个实例
template
void RegisterInstance() {
RegisterInstance(std::make_shared(GetObject()...));
}
现在已经准备好了。这里,将展示一个玩具使用场景,注册了两个对象——一个对象的构造函数将被调用并传入另一个类型的实例。将注意到必须在这里定义静态类型ID计数器,并给它一个初始值——使用什么值并不重要,但只想让它是一个容易识别的值。
IOCContainer gContainer;
// 用非零数字初始化
int IOCContainer::s_nextTypeId = 115094801;
class IAmAThing {
public:
virtual ~IAmAThing() {}
virtual void TestThis() = 0;
};
class IAmTheOtherThing {
public:
virtual ~IAmTheOtherThing() {}
virtual void TheOtherTest() = 0;
};
class TheThing : public IAmAThing {
public:
TheThing() {}
void TestThis() {
std::cout << "A Thing" << std::endl;
}
};
class TheOtherThing : public IAmTheOtherThing {
std::shared_ptr m_thing;
public:
TheOtherThing(std::shared_ptr thing) : m_thing(thing) {}
void TheOtherTest() {
m_thing->TestThis();
}
};
int main(int argc, const char* argv[]) {
gContainer.RegisterInstance();
gContainer.RegisterFactory();
gContainer.GetObject()->TheOtherTest();
return 0;
}