2012-08-17 76 views
4

我一直在阅读依赖注入,并且我理解如何在XML中指定依赖关系的吸引力,就像在许多框架中所做的那样。我在一个大型系统上工作,在这个系统中,我们通常会调用工厂来获取具体对象,并且正在努力理解为什么手动注入的依赖项为什么in this Wikipedia article被认为更好。为什么依赖注入比使用工厂更好?

在我看来,调用一个工厂是更好,因为:

  1. 调用代码不需要知道或关心一个特定的依赖关系存在。
  2. 调用代码并不需要改变,如果新的依赖被添加到被叫方。
  3. 调用代码并不需要有专门为选择具体的实例来注入任何逻辑。

在我看来,这种依赖注入仅提供好处,当调用代码决定对依赖具体的类。几乎就像“这是我的数据,现在处理它。”

有什么我错过了什么?

更新: 澄清,我们现有的代码主要是直接调用工厂。因此,要获得一个新的球对象,你会看到这样的代码:

Ball myBall = BallFactory.getObject(); 

很多的工厂都实现以允许新的具体对象类型的运行时间登记 - 一个插件框架。

所以在看一些看起来像DI的初步意见后,我调用的代码通常不会传中,球对象,而是将BallFactory。我想这样做的好处是,类可能更通用,因为它甚至没有耦合到它使用的工厂。

+7

您是否将那些工厂作为参数传递给需要的对象?那就是*依赖注入*。 – deceze 2012-08-17 13:57:19

+1

你是在谈论工厂类还是静态工厂方法?无论如何,工厂是你通常如何实现依赖注入。 – 2012-08-17 14:28:50

+0

依赖注入为您的工作,为你,不用你。这就是原因(假设你正在使用依赖注入框架)。 – 2012-08-17 15:30:50

回答

2

两者都不比另一个“更好”;您可以使用依赖注入本身,工厂本身或这些的组合。

您提到的关于工厂的所有3点对于依赖注入同样有效。请记住,依赖关系可以在任何时间点注入,而不一定由立即的“调用代码”注入。事实上,这是一个DI框架为你做的 - 它基本上是一个巨型工厂,它创建你的主应用程序对象并为你注入所有的依赖关系。

仅当代码需要能够在运行时创建依赖项的新实例时,才明确使用工厂。否则,简单地使用DI框架在应用程序启动期间注入所有静态依赖关系就简单多了。

3

依赖注入有助于在单元测试。它允许你分离和隔离你的类的功能,因为它的任何依赖可以被注入(因此也可以被模拟)到类中。如果依赖性访问外部资源(如DB),这特别有用。

最近我看到一篇文章,证明在测试依赖注入的优点。它具体是关于静态方法,但同样适用于工厂。

http://misko.hevery.com/2008/12/15/static-methods-are-death-to-testability/

像@deceze说不过,如果你注入你的工厂你得到两全其美...

+0

我不是Miško那篇文章的特别大粉丝。它确实提出了一个非常令人信服的观点,但这是IMO过于极端的做法。静态方法是非常有用的,你只需要小心*你*使用它们。在阅读时请记住这一点。 – deceze 2012-08-17 14:37:19

+0

我同意,这是极端的,但它使有效的点,mayb只需要一撮盐。 – Liam 2012-08-20 08:11:38

+0

您仍然可以将静态工厂设置为在运行时为您的单元测试进行配置。你不需要依赖注入来做到这一点。 – Sam 2014-08-01 06:34:06

1

使用依赖注入与否是有点像使用printf之间在C差异并使用fprintf。呼叫者以必须作出选择为代价获得灵活性。当调用者不需要灵活性时(例如,如果所有程序的输出都转换为stdout,而不是stderr或文件),灵活性是一种纯粹的负担,因为调用者必须始终传递相同的“正确”值。

如果您将依赖注入视为纯粹的负担,那意味着您的调用者并未真正使用它。

你的观点1和3都表示,“调用代码没有什么自由来影响发生的事情”,这并不总是一个优势。测试代码特别受益于注入依赖关系,但也可能出现其他原因需要灵活性的情况。你是否记录了printf,或者你是否通过调用注入记录器的函数来记录日志?

第2点归结为你如何发展你的API。是的,如果原始设计需要改变,那么通过使用固定的隐藏依赖关系,您可以屏蔽API以反映这一变化。有时你可以使用新的依赖项的默认值来维护旧的API,并在某处添加一个带有额外参数的新方法/构造函数。

所有这一切,标准库在我使用的语言不需要大量的依赖注入。所以你并不孤单,认为你的API不需要它,但是我怀疑你现在可以从内部获得更多的利益。例如,你可以在不连接远程机器的情况下测试你的网络代码吗?如果没有,请考虑如果可以的话,测试过程的这一部分是否会更容易,更快速并提供更准确的诊断。

2

结合使用依赖注入和抽象工厂通常是有用的 - 但出于两个独立的原因。使用(手动)依赖注入的原因是它允许您在单元测试期间注入一个特殊对象。如果您的设计描述调用代码不负责创建对象(从1-2-3项目符号开始),则提供的依赖项应为抽象工厂的实例。被注入的对象将使用工厂在需要时创建对象。

假设你使用两个工厂,生产的步步高容易和硬游戏的依赖关系(这里只是一个从属关系,骰子):

public class EasyGameFactory implements GameFactory 
{ 
    Dice createDice() 
    { 
    return new LuckyDice(); 
    } 
} 

public class NormalGameFactory implements GameFactory 
{ 
    Dice createDice() 
    { 
    return new RandomDice(); 
    } 
} 

为单元测试的目的,你会真的喜欢既不用骰子实现,所以你写了一个GameFactory的特殊实现:

public class CustomGameFactory implements GameFactory 
{ 
    private Dice mDice; 

    public CustomGameFactory(Dice dice) 
    { 
    mDice = dice; 
    } 

    Dice createDice() 
    { 
    return mDice; 
    } 
} 

这个工厂不一定是你的生产代码树的一部分。您通过测试代码向工厂提供骰子的特殊实现:

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()); 
    } 
}