【问题标题】:What is the difference between Inversion of Control and Dependency injection in C++?C ++中的控制反转和依赖注入有什么区别?
【发布时间】:2011-02-08 13:35:51
【问题描述】:

我最近一直在阅读有关 C++ 中的 DI 和 IoC 的内容。我有点困惑(即使在阅读了关于 SO 的相关问题之后)并希望得到一些澄清。

在我看来,熟悉 STL 和 Boost 会导致大量使用依赖注入。例如,假设我创建了一个函数来计算一系列数字的平均值:

template <typename Iter>
double mean(Iter first, Iter last)
{
    double sum = 0;
    size_t number = 0;
    while (first != last)
    {
        sum += *(first++);
        ++number;
    }
    return sum/number;
};

这是(即使用迭代器而不是访问集合本身)依赖注入吗?控制反转?都没有?

让我们看另一个例子。我们有一堂课:

class Dice
{
public:
    typedef boost::mt19937 Engine;
    Dice(int num_dice, Engine& rng) : n_(num_dice), eng_(rng) {}
    int roll()
    {
        int sum = 0;
        for (int i = 0; i < num_dice; ++i)
            sum += boost::uniform_int<>(1,6)(eng_);
        return sum;
    }
private:
    Engine& eng_;
    int n_;
};

这看起来像是依赖注入。但这是控制反转吗?

另外,如果我遗漏了什么,有人可以帮助我吗?这似乎是做事的自然方式,所以如果这就是依赖注入的全部内容,为什么人们很难使用它?

【问题讨论】:

  • 你读过维基吗?对IoC/DI有很明确的定义en.wikipedia.org/wiki/Inversion_of_control
  • 在 C++ 中,我们不做 IoC 或 DI - 我们有自己的自命不凡且名称错误的概念。
  • 如果您不小心,您的模板函数可能会被零除。
  • @Shaggy Frog:这是故意的 :)

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


【解决方案1】:

Inversion of Control 是一个非常通用的概念,根据您所谈论的“控制”类型而具有不同的含义。依赖注入是一种特定的形式。

控制反转和迭代

在这种情况下,“控制”意味着“流量控制”。

我认为您的第一个涉及迭代的示例并不是真正的控制反转,因为该代码明确地进行了流程控制。控制反转会将要执行的操作与流控制分开。它可能看起来像这样(请原谅我的 java/C#):

SumVisitor sumVisitor = new SumVisitor();
collection.AcceptVisitor(sumVisitor);
int sum = sumVisitor.GetSum();

访问者对象为它访问的每个集合元素做一些事情,例如更新总和计数器字段。但它无法控制集合如何或何时调用它,因此控制反转。您还可以实现MedianVisitorMeanVisitorMaximumVisitor 等。每个都实现了一个通用的IVisitor 接口和一个Visit(Element) 方法。

对于集合,情况正好相反:它不知道访问者做了什么,只是通过为集合中的每个元素调用visitor.Visit(element) 来处理流控制。集合的不同访问者实现看起来都一样。

控制反转和对象图构造

在这种情况下,“控制”是指“控制组件的创建和连接方式”。

在任何重要的应用程序中,代码都被拆分为必须协作的组件。为了保持组件可重复使用,它们不能直接相互创建,因为这会将它们永久地粘合在一起。相反,单个组件放弃了对构造和组件接线的控制。

Dependency injection 是实现此目的的一种方法,通过在构造函数中引用合作者对象。然后,您需要一段单独的启动代码,在其中创建所有组件并将其连接在一起,或者需要一个依赖注入框架来为您处理这个问题。你的 Dice 类确实是依赖注入的一个例子。

另一种放弃控制对象图构造的方法是Service Locator 模式,尽管它有disadvantages

【讨论】:

    【解决方案2】:

    让我试着回答。

    你的第一个例子两者都不是。它只是一个模板。

    要成为依赖注入,必须选择一个实现,并将其提供给模板。

    要使其成为 IoC,必须在运行时(而不是编译时)将模板提供给 IMPLEMENTATION 类型,并用作“mean()”函数的实现(想想提供 mean 函数的工厂实现)

    您的第二个示例看起来像是 DI/IoC 的消费者。将 Engine 的实现发送到您的类的代码将是 DI/IoC 组件。

    希望这是准确的,并且会有所帮助。

    【讨论】:

      猜你喜欢
      • 2015-01-09
      • 1970-01-01
      • 2010-09-13
      • 1970-01-01
      • 2012-02-05
      • 1970-01-01
      • 2013-09-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多