Inversion of control是一种防值技术,用于模块化系统并将组件相互分离。
低耦合始终是一个优势:它简化了组件的自动测试,使代码更符合single responsibility principle。
在向另一个类(service locator,属性注入调用公共方法/设置公共属性...)中声明依赖的方法中,构造函数注入似乎是最好的方法。
尽管它可能是最难实现的(至少来自列出的三个),但它具有显着的优势:
C ++提供的许多选择通过构造函数执行注入的优缺点是什么?
答案 0 :(得分:11)
class object
{
public:
object(dependency d) : dep_(d) {}
private:
dependency dep_;
};
仅在dependency
类完全无状态时才起作用,即没有任何成员。实际上,这很少发生,因为dependency
类可以存储自己的依赖。
class object
{
public:
object(dependency *d) : dep_(d)
{
if (d == nullptr)
throw std::exception("null dependency");
}
private:
dependency *dep_;
};
这就像真正的注射一样。我们需要检查nullptr
值的传递指针。
object
类不拥有dependency
类,因此调用代码以确保object
在dependency
对象之前被销毁。
在实际应用中,有时很难验证。
#define DISALLOW_COPY_AND_ASSIGN(Class) \
Class(const Class &) = delete; \
Class &operator=(const Class &) = delete
class object
{
public:
object(dependency &d) : dep_(d) {}
DISALLOW_COPY_AND_ASSIGN(object);
private:
dependency &dep_;
};
引用不能为空,因此在这个预期中它会更安全一些。
然而,这种方法为object
类带来了额外的约束:它必须是不可复制的,因为无法复制引用。您必须手动覆盖赋值运算符和复制构造函数,以阻止复制或从boost::noncopyable
等内容继承它。
与原始指针一样,所有权约束已到位。调用代码应为两个类提供正确的销毁顺序,否则引用将变为无效,应用程序将因访问冲突而崩溃。
如果依赖项是const引用:
class object
{
public:
object(const dependency &d) : dep_(d) {}
private:
const dependency &dep_;
};
你应该注意object
类接受对临时对象的引用这一事实:
dependency d;
object o1(d); // this is ok, but...
object o2(dependency()); // ... this is BAD.
进一步详情:
class object
{
public:
object(std::shared_ptr<dependency> d) : dep_(d)
{
if (!d)
throw std::exception("null dependency");
}
private:
std::shared_ptr<dependency> dep_;
};
与原始指针类似,但所有权由智能指针机制控制。
仍然需要在构造函数体中检查nullptr
。
主要优点是dependency
对象生存期控制:调用应用程序无需正确控制销毁顺序(但请考虑you need to be very careful when designing your APIs with std::shared_ptr
)。
一旦dependency
类不再使用,它将被shared_ptr
析构函数自动销毁。
有些情况下shared_ptr
拥有的对象未被销毁(所谓的cyclic references)。但是,使用构造函数注入时,由于特定的明确定义的构造顺序,循环依赖性是不可能的。
如果在整个应用程序中没有使用其他注入方法,这当然有效。
智能指针的开销很小,但在大多数情况下并不是真正的问题。
进一步详情: