控制容器的转换允许系统通过间接指定对象的依赖关系来实现依赖关系注入。通常,IOC容器将负责具体类型的实际实例化。
介绍
控制容器的反转允许系统通过间接指定对象的依赖关系来实现依赖关系注入。通常,IOC容器将负责具体对象的实际实例化,选择构造函数并仅返回引用类型,在调用构造函数之前获取任何依赖项的实例。今天,我做了一个小游戏,想和大家分享一下。
获取类型代码
在安全地存储获取订阅特定接口的类的方法之前,我们应该为此分配一个类型代码。在IOC容器的实现中,我有一个静态整型变量,用于指定要分配的下一个类型id,以及每个类型的一个静态局部变量实例,可以通过调用GetTypeID方法来访问该实例。
class IOCContainer
{
static int s_nextTypeId;
public:
//one typeid per type
template<typename T>
static int GetTypeID()
{
static int typeId = s_nextTypeId ++;
return typeId;
}
获取对象实例
好吧,那很简单。既然我们有了类型ID,我们应该能够存储某种工厂对象来表示我们不知道如何创建对象的事实。因为我想将所有工厂存储在同一个集合中,所以我选择工厂将从中派生的抽象基类,以及捕获函子以供以后调用的实现。
为简洁起见,我使用了std::map来保留工厂,但是为了提高效率,您可能会考虑其他选项。如果只构建一次集合,然后多次使用它,那么std::pair的排序std::vector将非常有意义。
class FactoryRoot
{
public:
virtual ~FactoryRoot() {}
};
//todo: consider sorted vector
std::map<int, std::shared_ptr<FactoryRoot>> m_factories;
template<typename T>
class CFactory: public FactoryRoot
{
std::function<std::shared_ptr<T> ()> m_functor;
public:
~CFactory() {}
CFactory(std::function<std::shared_ptr<T> ()> functor)
:m_functor(functor)
{
}
std::shared_ptr<T> GetObject()
{
return m_functor();
}
};
template<typename T>
std::shared_ptr<T> GetObject()
{
auto typeId = GetTypeID<T>();
auto factoryBase = m_factories[typeId];
auto factory = std::static_pointer_cast<CFactory<T>>(factoryBase);
return factory->GetObject();
}
注册实例
现在我们需要做的就是填充集合。我实现了几种不同的方法:您可能希望显式地提供一个functor,或者您可能希望在单个实例之间进行选择,或者根据需要创建新实例。
//Most basic implementation - register a functor
template<typename TInterface, typename ...TS>
void RegisterFunctor(std::function<std::shared_ptr<TInterface>
(std::shared_ptr<TS> ...ts)> functor)
{
m_factories[GetTypeID<TInterface>()] =
std::make_shared<CFactory<TInterface>>([=]{return functor(GetObject<TS>()...);});
}
//Register one instance of an object
template<typename TInterface>
void RegisterInstance(std::shared_ptr<TInterface> t)
{
m_factories[GetTypeID<TInterface>()] =
std::make_shared<CFactory<TInterface>>([=]{return t;});
}
//Supply a function pointer
template<typename TInterface, typename ...TS>
void RegisterFunctor(std::shared_ptr<TInterface> (*functor)(std::shared_ptr<TS> ...ts))
{
RegisterFunctor(std::function<std::shared_ptr<TInterface>
(std::shared_ptr<TS> ...ts)>(functor));
}
//A factory that will call the constructor, per instance required
template<typename TInterface, typename TConcrete, typename ...TArguments>
void RegisterFactory()
{
RegisterFunctor(
std::function<std::shared_ptr<TInterface> (std::shared_ptr<TArguments> ...ts)>(
[](std::shared_ptr<TArguments>...arguments) -> std::shared_ptr<TInterface>
{
return std::make_shared<TConcrete>
(std::forward<std::shared_ptr<TArguments>>(arguments)...);
}));
}
//A factory that will return one instance for every request
template<typename TInterface, typename TConcrete, typename ...TArguments>
void RegisterInstance()
{
RegisterInstance<TInterface>(std::make_shared<TConcrete>(GetObject<TArguments>()...));
}
};
使用场景
好的,我们差不多准备好了。在这里,我将展示一个玩具使用场景,在这里我注册了两个对象——一个带有构造函数,该构造函数将被调用并在另一个类型的实例中传递。您会注意到,我必须在这里定义静态类型id计数器,并给它一个初始值—在这里使用什么值并不重要,但我只希望它是易于识别的。
IOCContainer gContainer;
//initialise with nonzero number
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<IAmAThing> m_thing;
public:
TheOtherThing(std::shared_ptr<IAmAThing> thing):m_thing(thing)
{
}
void TheOtherTest()
{
m_thing->TestThis();
}
};
int main(int argc, const char * argv[])
{
gContainer.RegisterInstance<IAmAThing, TheThing>();
gContainer.RegisterFactory<IAmTheOtherThing, TheOtherThing, IAmAThing>();
gContainer.GetObject<IAmTheOtherThing>()->TheOtherTest();
return 0;
}