Answers:
您称其为“勤奋的软件开发”,而我称其为“痛苦的过度设计”。这并不是说控制反转是不好的-实际上,控制权的基本定义是好的-但是整个框架和实现所有这些目标的工作方法的泛滥几乎是疯狂的,特别是与人们浪费的方式相结合完美而干净的界面,以便能够在99%的时间里注入您永远不会互换的可互换组件。这种事情只能起源于Java企业环境,我很高兴它在其他地方没有那么大的立足点。
通常的论点是,即使您不交换组件,也希望能够与模拟对象等进行隔离测试。但是,恐怕我永远也不会接受这样的说法:为了更好地测试它,值得膨胀和使接口复杂化。测试仅证明一件事-测试有效。另一方面,干净和最少的接口在证明您的代码可以正常工作方面大有帮助。
所以,简短的答案是:是的,但不是您的思维方式。有时,当您需要可互换的行为时,会将一个对象传递给一个指示新对象行为一部分的构造函数。就是这样
策略模式,组合,依赖注入都密切相关。
由于策略模式是依赖注入的一种形式,因此,例如,如果您看看像Unity这样的引擎,它们便完全基于此原理。他们对组件(策略模式)的使用被深深地嵌入到他们的整个引擎中。
除了重用组件外,主要好处之一是避免了可怕的深层类层次结构。
这是Mick West的一篇文章,谈到了他如何将这种类型的系统引入Neversoft 的Tony Hawk系列游戏中。
直到最近几年,游戏程序员一直使用深层次的类层次结构来表示游戏实体。潮流开始从使用深层次结构转变为将游戏实体对象构成为组件聚合的各种方法...
关于控制反转(IoC)模式似乎有很多困惑。许多人将其等同于“策略模式”或“组件模型”,但这些比较并没有真正体现IoC的含义。IoC实际上是关于如何获得依赖关系的。让我给你举个例子:
class Game {
void Load() {
this.Sprite.Load(); // loads resource for drawing later
}
}
class Sprite {
void Load() {
FileReader reader = new FileReader("path/to/resource.gif");
// load image from file
}
}
在上面很明显,Sprite.Load
它依赖于FileReader
。当您要测试方法时,您需要:
前两个很明显,但是如果您要确保错误处理能够按预期工作,那么您也确实需要#3。在这两种情况下,由于它们现在都需要进入磁盘,因此可能会大大降低测试速度,并且可能会使测试环境更加复杂。
IoC的目标是使行为的使用与其构造脱钩。请注意,这与策略模式有何不同。使用策略模式的目标是封装行为的可重用块,以便将来可以轻松扩展它;它无话可说。
如果我们要重写Sprite.Load
上面的方法,我们可能会得到以下结果:
class Sprite {
void Load(IReader reader) {
// load image through reader
}
}
现在,我们已经将阅读器的构造与使用分开了。因此,可以在测试期间交换测试读取器。这意味着您的测试环境不再需要文件系统,测试文件,并且可以轻松地模拟错误事件。
请注意,我在重写中做了两件事。我创建了一个IReader
封装了某些行为的接口-即实现了Strategy模式。另外,我将创建合适的读者的职责转移到了另一个班级。
也许我们不需要新的模式名称来描述上述内容。它给我留下了混合的策略和工厂模式(适用于IoC容器)。话虽这么说,但我不确定人们会基于什么理由反对这种模式,因为很明显它可以解决一个实际问题,而且,当然,这对我来说与Java无关。
在这一点上,我必须同意Kylotan。“依赖注入”是解决丑陋的Java概念缺陷的丑陋的Java解决方案,而任何人都用其他语言看待它的唯一原因是因为Java成为了很多人的第一语言,而实际上却不是。
另一方面,控制反转是一个有用的想法,已经存在了很长时间,如果做对了,这将非常有帮助。(不是Java / Dependency Injection方法。)实际上,如果您使用的是明智的编程语言,那么您可能一直都在这样做。当我第一次阅读每个人都在嗡嗡作响的整个“ IOC”时,我完全不知所措。控制反转无非就是将函数指针(或方法指针)传递给例程或对象以帮助自定义其行为。
这是自1950年代以来一直存在的想法。它在C标准库中(想到了qsort),并且在Delphi和.NET(事件处理程序/代理)中到处都是。它使旧代码可以调用新代码而无需重新编译旧代码,并且它一直在游戏引擎中得到使用。