基本概念
例子很简单,注入的方式也有很多方法,包括构造注入、set注入等方法,在此基础上应用依赖倒置(Dependency Inversion Principle)原则,SOLID原则之一。
类A依赖类B的抽象接口,而不面向具体类B编程,实现类A与类B的解耦。
上述将依赖类的构造逻辑与业务逻辑分离,类A依赖类B的抽象接口,那么接下来的问题如何构造依赖类?
app实现:
B b{...};
C c{...};
A a(b, c);
a.Process(...);
此时app 实现时需要包含 B、C、A类的具体类,但实际app仅使用了类A业务类,并不需要看到类B和类C,但此时为了创建类B、类C等依赖类,不可避免的要引入其头文件,给app造成不必要的负担;
常用的解决方法为将class B 注入到Factory,class A通过Factory获取class B,Factory中存储map<key,ConstructorFunc>,即Dependency Inject Container,如此一来app不需要关注依赖类的创建,业务类A和依赖类B也是解耦的。
应用实践
通过上述分析,可以实现一个抽象工厂如下:
template<typename BaseType>
class GeneralFactory {
struct CreatorBase {
std::set<std::type_index> m_set;
};
template<typename ...Ts>
struct CreatorWithArgs : CreatorBase {
CreatorWithArgs()
{
CreatorBase::m_set.insert(std::type_index(typeid(CreatorWithArgs<Ts...>)));
}
virtual std::shared_ptr<BaseType> Create(const Ts&... args) = 0;
};
template<typename T, typename ...Args>
struct CreatorHelper : CreatorWithArgs<Args...> {
std::shared_ptr<BaseType> Create(const Args&... args)
{
return std::make_shared<T>(args...);
}
};
public:
template<typename T, typename ...Args>
struct RegisterHelper {
RegisterHelper(const char *name)
{
GeneralFactory<BaseType>::GetInstance().Register(name, std::make_shared<CreatorHelper<T, Args...>>());
}
};
void Register(const char *name, std::shared_ptr<CreatorBase> creator)
{
if (m_creator.count(name) != 0) {
ERR_LOG("%s is already exist", name);
return;
}
m_creator[name] = creator;
}
template<typename ...Ts>
std::shared_ptr<BaseType> Create(const std::string& name, Ts&& ...args)
{
if (m_creator.count(name) == 0) {
ERR_LOG("%s not find", name.c_str());
return nullptr;
}
CreatorBase *base = m_creator[name].get();
if (base->m_set.find(std::type_index(typeid(CreatorWithArgs<Ts...>))) == base->m_set.end()) {
ERR_LOG("args mismatch");
return nullptr;
}
auto creator = static_cast<CreatorWithArgs<Ts...> *>(base);
return creator->Create(std::forward<Ts>(args)...);
}
static GeneralFactory& GetInstance()
{
static GeneralFactory factory {};
return factory;
}
private:
GeneralFactory() = default;
~GeneralFactory() = default;
std::map<std::string, std::shared_ptr<CreatorBase>> m_creator;
};
#define REGISTER_CLASS(_baseType, _class, ...) \
GeneralFactory<_baseType>::RegisterHelper<_class, ##__VA_ARGS__> reg##_class(#_class);
#define GET_CLASS(_baseType, _class, ...) \
GeneralFactory<_baseType>::GetInstance().Create(#_class, ##__VA_ARGS__);
class B通过MSG_REGISGER_CLASS注入到抽象工厂中,class A 在使用时内部通过MSG_GET_CLASS获取依赖类。
开源实现
[Boost::ext].DI:Your C++14 header only Dependency Injection library with no dependencies 【1】
DI可以自动识别需要构造的依赖类,并提供依赖类的入参绑定,通过下面的example进行简要介绍:
namespace di = boost::di;
struct interface {
virtual ~interface() noexcept = default;
virtual void dummy() = 0;
};
struct implementation : interface {
void dummy() override { }
};
struct uniform {
bool &b;
std::shared_ptr<interface> sp;
};
class direct {
public:
direct(const uniform &uniform, std::shared_ptr<interface> sp) : uniform_(uniform), sp_(sp) {}
const uniform &uniform_;
std::shared_ptr<interface> sp_;
};
class example {
public:
example(std::unique_ptr<direct> d, interface &ref, int i) : i_(i) {
assert(false == d->uniform_.b);
assert(d->sp_.get() == d->uniform_.sp.get());
assert(&ref == d->sp_.get());
}
auto run() const { return i_ == 42; }
private:
int i_ = 0;
};
int main() {
auto runtime_value = false;
// clang-format off
auto module = [&] {
return di::make_injector(
di::bind<>().to(runtime_value)
);
};
auto injector = di::make_injector(
di::bind<interface>().to<implementation>() // interface的实现类为implementation
, di::bind<>().to(42) // 根据自动类型推导,int 类型入参都为42,即example的int入参
, module() // uniform 中的b赋值为runtime_value
);
// clang-format on
/*<<create `example` - member function call>>*/
{
auto object = injector.create<example>();
assert(object.run());
}
/*<<create `example` - free function call>>*/
{
auto object = di::create<example>(injector);
assert(object.run());
}
}
上述代码中类的依赖关系为:
- app的创建依赖std::unique_ptr<direct>
- direct的创建依赖const uniform &, std::shared_ptr<interface>
- uniform的创建依赖 bool &,std::shared_ptr<interface>;
通过make_injector创建injector,将创建依赖类需要的参数进行绑定,再通过injector.create创建example。
其中bind的说明为: Bindings define dependencies configuration describing what types will be created and what values will be passed into them.
如果入参为相同类型,但是需要绑定不同的值,则可以使用BOOST_DI_INJECT,修改为:
class direct {
public:
BOOST_DI_INJECT(direct, const uniform &uniform, std::shared_ptr<interface> sp, (named = direct_int) int val): uniform_(uniform), sp_(sp) {
// direct(const uniform &uniform, std::shared_ptr<interface> sp, int val) : uniform_(uniform), sp_(sp) {
printf("%d \n", val);
}
const uniform &uniform_;
std::shared_ptr<interface> sp_;
};
此时direct和example构造入参都有一个int类型,在创建injector时可修改为:
auto injector = di::make_injector(
di::bind<interface>().to<implementation>()
, di::bind<>().to(runtime_value)
, di::bind<>().to(42) // 其他int 入参默认绑定为42
, di::bind<>().named(direct_int).to(36) // 指定名为direct_int的变量绑定为36
);
库中也支持模板类的模板参数绑定,后续再展开分析。
参考资料
【1】https://boost-ext.github.io/di/tutorial.html
标签:std,依赖,di,sp,uniform,Dependency,shared,Injection,class From: https://blog.51cto.com/u_13137973/9127448