通过构造函数进行依赖注入的最佳实践

时间:2016-09-01 09:12:30

标签: c++ dependency-injection inversion-of-control constructor-injection

Inversion of control是一种防值技术,用于模块化系统并将组件相互分离。

低耦合始终是一个优势:它简化了组件的自动测试,使代码更符合single responsibility principle

在向另一个类(service locator,属性注入调用公共方法/设置公共属性...)中声明依赖的方法中,构造函数注入似乎是最好的方法。

尽管它可能是最难实现的(至少来自列出的三个),但它具有显着的优势:

  • 使用构造函数签名真正可见所有依赖项;
  • 由于明确定义的实例化顺序,
  • 循环依赖不会发生。

C ++提供的许多选择通过构造函数执行注入的优缺点是什么?

1 个答案:

答案 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类,因此调用代码以确保objectdependency对象之前被销毁。

在实际应用中,有时很难验证。

参考

#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)。但是,使用构造函数注入时,由于特定的明确定义的构造顺序,循环依赖性是不可能的。

如果在整个应用程序中没有使用其他注入方法,这当然有效。

智能指针的开销很小,但在大多数情况下并不是真正的问题。

进一步详情: