在此上下文中使用依赖注入(控制反转)意味着什么(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):
解决方案:
当我在Stackoverflow上搜索关于DI的答案时,通常会在Java的上下文中询问。一些示例使用Java GUI。通常这些例子是如此简单和明显,以至于我没有看到它的重要性,除了更好的设计和更少的耦合。但是,我想要学习的是它背后的意义。正如wiki中所定义的,控制反转(IoC)意味着您可以反转代码控制流。那么,它如何适用于Google案例?与程序风格相比,实际流量如何反转?我认为代码是从上到下逐行执行,而不是从下到上依次执行?
答案 0 :(得分:3)
DI在这里意味着您的程序没有对类似LOGO的api的硬编码依赖关系,但是此依赖关系在运行时通过接口“注入”。这样,可以通过Mock API替换api以进行单元测试。
“控制反转”在这里意味着:如果程序中有一个函数需要类似LOGO的API的“图形上下文对象”,它不应该自己实例化该对象。相反,它应该将上下文对象作为参数(键入“Turtle”接口)给出,并且可以通过IoC框架完成对象创建。
这将与您在上面的“客户地址”示例中显示的方式相同,将clsAddress
替换为clsGraphicsContext
。
答案 1 :(得分: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.py和https://github.com/rhdunn/cainteoir-engine/blob/master/tests/metadata.py)推动此操作。
答案 2 :(得分:2)
“控制反转”中的反转不是代码执行顺序的反转,而是对象创建。在不使用“依赖注入”的体系结构中,对象将自己创建它们的依赖项。相反,当使用DI时,对象将从外部接收它们的依赖关系,并使用接口(C ++中的抽象类)来实现独立性。
在LOGO示例中,通过使用接口和setter而不是直接创建API包装器,您允许调用您的代码提供其接口实现。这样,测试代码(通过提供记录所有调用的模拟实现)或使用其他实现更容易。
答案 3 :(得分:1)
在我看来,你有点“等同”依赖注入和控制反转。
现在,据我所知,依赖注入是一种设计模式(粗略地说),你将指向对象的指针“注入”另一个对象;这将使第二个依赖于第一个(即,它需要知道前者的接口,并且将受到接口级别的变化的影响)。因此,它是一个非常通用的概念,可以在许多不同的环境中提供帮助。
从这个意义上讲,依赖注入只是实现控制反转的一种方式。例如,当您将回调注入对象时,就会发生这种情况,以便在适当的时刻调用它们。
在您的具体示例中,我怀疑依赖注入被用作获取控制反转的一种方式,因为在我看来控制是不会被反转的(例如,当您有回调或框架时)
正如您所说的那样,更多的情况是添加中间层以实现更好的去耦(也可以通过依赖注入获得)。