我一直在阅读依赖注入,我理解在XML中指定依赖项的吸引力,就像许多框架中所做的那样。我在一个大型系统上工作,我们通常会调用工厂来获取具体对象,并且很难理解为什么手动注入依赖关系,如图所示in this Wikipedia article应该更好。
在我看来,打电话给工厂更好,因为:
在我看来,当调用代码具有来决定依赖项的具体类时,依赖注入只会带来好处。几乎就像“这是我的数据,现在处理它。”
我错过了什么吗?
更新 为了澄清,我们现有的代码主要是直接调用工厂。因此,要获得一个新的Ball对象,您会看到如下代码:
Ball myBall = BallFactory.getObject();
许多工厂被实现为允许运行时注册新的具体对象类型 - 插件框架。
所以在查看一些初始注释后,看起来像DI我的调用代码通常不会传入Ball对象,而是BallFactory。我想这样做的好处是该类可能更通用,因为它甚至没有耦合到它使用的工厂。
答案 0 :(得分:3)
依赖注入有助于单元测试。它允许您分离和隔离类的功能,因为它的任何依赖项都可以注入(因此也被模拟)到类中。如果依赖项访问外部资源(例如DB),这将特别有用。
我最近读了一篇文章,证明了依赖注入在测试中的优势。它特别是关于静态方法,但同样适用于工厂。
http://misko.hevery.com/2008/12/15/static-methods-are-death-to-testability/
就像@deceze所说,如果你注入你的工厂,你将获得两全其美......
答案 1 :(得分:2)
两者都没有“好”;你可以单独使用依赖注入,单独使用工厂,或者使用它们的组合。
您提到的有关工厂的所有3点对于依赖注入同样有效。请记住,依赖关系可以在任何时间点注入,而不一定是直接的“调用代码”。事实上,这就是DI框架为您所做的事情 - 它基本上是一个巨大的工厂,它创建您的主应用程序对象并为您注入所有依赖项。
只有当您的代码需要能够在运行时创建依赖项的新实例时,显式使用工厂才有用。否则,在应用程序启动期间简单地使用DI框架注入所有静态依赖项会简单得多。
答案 2 :(得分:2)
将依赖注入和抽象工厂结合使用通常很有用 - 但原因有两个。使用(手动)依赖注入的原因是它允许您在单元测试期间注入特殊对象。如果您的设计描述调用代码不负责创建对象(从您的1-2-3项目符号开始),那么提供的依赖项应该是抽象工厂的实例。注入的对象将使用工厂在需要时创建对象。
假设你使用两个工厂为步步高的简单和硬游戏产生依赖关系(这里只有一个依赖关系,Dice):
public class EasyGameFactory implements GameFactory
{
Dice createDice()
{
return new LuckyDice();
}
}
public class NormalGameFactory implements GameFactory
{
Dice createDice()
{
return new RandomDice();
}
}
对于单元测试目的,你真的更喜欢不使用Dice实现,所以你编写了一个特殊的GameFactory实现:
public class CustomGameFactory implements GameFactory
{
private Dice mDice;
public CustomGameFactory(Dice dice)
{
mDice = dice;
}
Dice createDice()
{
return mDice;
}
}
此工厂不必是生产代码树的一部分。您可以通过测试代码为工厂提供特殊的Dice实现:
public class TestBackgammon
{
@Test public void shouldReturnDiceThrown()
{
SettableDice dice = new SettableDice();
Game game = new GameImpl(new CustomGameFactory(dice));
dice.setDice(new int[] {4, 5});
game.nextTurn();
assertArrayEquals(new int[] {4, 5}, game.diceThrown());
}
}
答案 3 :(得分:1)
使用依赖注入与否有点像使用printf
和使用fprintf
之间的C差异。呼叫者以必须做出选择为代价获得灵活性。当调用者不想要灵活性时(例如,如果所有程序的输出都转到stdout,从不stderr或文件),灵活性是一个纯粹的负担,因为调用者总是必须传递相同的“正确”值。
如果你认为依赖注入是一种纯粹的负担,那就意味着你的调用者并没有真正使用它。
你的观点1和3都说,“主叫代码影响发生的事情的自由度较低”,这并不总是有利的。测试代码特别受益于注入依赖项,但也可能存在调用者出于其他原因需要灵活性的情况。您是使用printf
登录还是通过调用注入的记录器上的函数进行记录?
第2点归结为您如何发展您的API。是的,如果原始设计需要通过使用固定的隐藏依赖项进行更改,则可以防止API反映该更改。有时您可以使用新依赖项的默认值维护旧API,并在某处添加一个带有额外参数的新方法/构造函数。
所有这一切,我用过的语言中的标准库并不需要大量的依赖注入。因此,您并不是唯一一个认为您的API不需要它的人,但我怀疑您可以从内部获得比现在更多的API。例如,您是否可以在不连接远程计算机的情况下测试网络代码?如果没有,请考虑您的测试程序的这一部分是否更容易,更快,并提供更准确的诊断,如果可以的话。