C ++ Google Mock指南提到的依赖注入

时间:2011-12-30 09:27:21

标签: c++ dependency-injection

在此上下文中使用依赖注入(控制反转)意味着什么(Google Mock):

  

让我们看一个例子。假设您正在开发图形   依赖于类似LOGO的API进行绘制的程序。你会怎么测试   它做对了吗?好吧,你可以运行它并比较   屏幕上有一个金色的屏幕快照,但让我们承认:测试就像   这是昂贵的运行和脆弱(如果你刚刚升级到   闪亮的新显卡具有更好的抗锯齿效果?突然间你   必须更新你所有的黄金图像。)如果这太痛苦了   你所有的测试都是这样的。幸运的是,你了解到了   依赖注入并知道正确的事情:而不是拥有   您的应用程序直接与绘图API对话,将API包装在一个   接口(比方说,Turtle)和该接口的代码

class Turtle {
  ...
  virtual ~Turtle() {}
  virtual void PenUp() = 0;
  virtual void PenDown() = 0;
  virtual void Forward(int distance) = 0;
  virtual void Turn(int degrees) = 0;
  virtual void GoTo(int x, int y) = 0;
  virtual int GetX() const = 0;
  virtual int GetY() const = 0;
};

在应用程序代码和类别绘制API之间添加另一层时,它与DI有什么关系??在许多关于依赖注入的Java示例中,通常不应在类中具体创建对象。相反,它应该在别处创建,以分隔两个对象之间的实现耦合。例如(来自codeproject):

enter image description here

解决方案:

enter image description here

当我在Stackoverflow上搜索关于DI的答案时,通常会在Java的上下文中询问。一些示例使用Java GUI。通常这些例子是如此简单和明显,以至于我没有看到它的重要性,除了更好的设计和更少的耦合。但是,我想要学习的是它背后的意义。正如wiki中所定义的,控制反转(IoC)意味着您可以反转代码控制流。那么,它如何适用于Google案例?与程序风格相比,实际流量如何反转?我认为代码是从上到下逐行执行,而不是从下到上依次执行?

4 个答案:

答案 0 :(得分:3)

DI在这里意味着您的程序没有对类似LOGO的api的硬编码依赖关系,但是此依赖关系在运行时通过接口“注入”。这样,可以通过Mock API替换api以进行单元测试。

“控制反转”在这里意味着:如果程序中有一个函数需要类似LOGO的API的“图形上下文对象”,它不应该自己实例化该对象。相反,它应该将上下文对象作为参数(键入“Turtle”接口)给出,并且可以通过IoC框架完成对象创建。

这将与您在上面的“客户地址”示例中显示的方式相同,将clsAddress替换为clsGraphicsContext

答案 1 :(得分:3)

维基百科对这些有很好的定义:

  1. http://en.wikipedia.org/wiki/Inversion_of_control
  2. http://en.wikipedia.org/wiki/Dependency_injection
  3. 依赖注入只是一种奇特的方式,表示类通过接口(Graphics API)与另一个进行交互,并且它提供了一种更改接口所指向的方式(即注入另一个类的依赖关系)。 / p>

    对于控制反转,维基百科提到像工厂模式这样的东西。

    它还提到了setter注入(使用setter函数更改接口实现),构造注入(从构造函数设置接口实现)或接口注入(从另一个接口请求接口实现)并注意到这些是依赖类型注射。

    这就是这里发生的事情 - 程序可以使用setter方法(Inversion of Control)更改海龟程序(依赖注入)的绘图API。

    这允许您拥有这样的测试类:

    struct DrawingTester : public DrawingInterface
    {
        void move_to(long x, long y) { printf("moveto %d %d\n", x, y); }
        void line_to(long x, long y) { printf("lineto %d %d\n", x, y); }
    };
    

    并通过测试程序开车:

    int main(int argc, char **argv) {
        DrawingTester drawing;
        Turtle t;
        t.setDrawingApi(&drawing);
        t.runProgramFromFile(argv[0]);
        return 0;
    }
    

    然后,您可以使用DrawingTester的预期输出获得turtle / logo测试程序。例如:

    # test1.logo
    
    MOVE 5, 7
    
    # test1.calls
    
    moveto 5 7
    

    并通过测试套件(例如https://github.com/rhdunn/cainteoir-engine/blob/master/tests/harness.pyhttps://github.com/rhdunn/cainteoir-engine/blob/master/tests/metadata.py)推动此操作。

答案 2 :(得分:2)

“控制反转”中的反转不是代码执行顺序的反转,而是对象创建。在不使用“依赖注入”的体系结构中,对象将自己创建它们的依赖项。相反,当使用DI时,对象将从外部接收它们的依赖关系,并使用接口(C ++中的抽象类)来实现独立性。

在LOGO示例中,通过使用接口和setter而不是直接创建API包装器,您允许调用您的代码提供其接口实现。这样,测试代码(通过提供记录所有调用的模拟实现)或使用其他实现更容易。

答案 3 :(得分:1)

在我看来,你有点“等同”依赖注入和控制反转。

现在,据我所知,依赖注入是一种设计模式(粗略地说),你将指向对象的指针“注入”另一个对象;这将使第二个依赖于第一个(即,它需要知道前者的接口,并且将受到接口级别的变化的影响)。因此,它是一个非常通用的概念,可以在许多不同的环境中提供帮助。

从这个意义上讲,依赖注入只是实现控制反转的一种方式。例如,当您将回调注入对象时,就会发生这种情况,以便在适当的时刻调用它们。

在您的具体示例中,我怀疑依赖注入被用作获取控制反转的一种方式,因为在我看来控制是不会被反转的(例如,当您有回调或框架时)

正如您所说的那样,更多的情况是添加中间层以实现更好的去耦(也可以通过依赖注入获得)。