依赖注入是一种模式,是吗?

时间:2010-06-30 00:39:20

标签: design-patterns dependency-injection inversion-of-control

我和我的一位同事就依赖注射问题进行了激烈辩论,并意识到我并不完全了解有关该主题的所有事实。

所以,拿这个代码(只是你知道,我们正在使用Castle Windsor)

IPlayerService service = Container.Resolve<IPlayerService>();

上面的代码显然是使用IoC的DI的一个例子。

但是,请参阅下面的代码(更新:假设我通过构造函数传递所有外部依赖项):

var playerClient = new PlayerClient();
var playerSkinClient = new PlayerSkinClient();
IPlayerService service = new PlayerService(playerClient, playerSkinClient);

我的论点是上面的代码是DI模式的一个例子,DI可以在没有IoC的情况下存在。

现在,我的同事并没有完全不同意我的观点,但他说上面的代码不是涉及DI的任何模式的例子。

  1. 那么,DI可以作为一种模式而不需要任何额外的框架吗?

  2. 如果是,上面的代码是一个例子吗?

  3. 最后,定义了DI模式(如果存在),没有容器的概念。

  4. 更新

    我今晚稍后会更全面地回答和评论,但感谢大家到目前为止经过深思熟虑的答案和评论!

8 个答案:

答案 0 :(得分:15)

根据Martin Fowler所说,Dependency Injection是抽象概念的具体实现的另一个名称,称为控制反转(full article)。

从根本上说,所有DI意味着:一个类依赖于正常工作的对象被初始化并从外部传递到它,而不是类本身负责获取/初始化这些对象。如何实现这一点的具体细节超出了模式的范围。

IoC是同样的事情,但正如Martin Fowler所说,“控制反转”是描述正在发生的事情的一种非常迟钝的方式。

我个人认为“依赖注入”并不是更好。我会将其描述为“正确使用构造函数”。

非IoC /非DI的示例:

class Foo
{
    void DoSomething()
    {
        DbConnection myConnection = new DbConnection(ConfigurationSettings...);
        myConnection.ExecuteCommand();
    }
}

使用IoC / DI也是如此:

class Foo
{
    private DbConnection myConnection;

    public Foo(DbConnection conn)
    {
        myConnection = conn;
    }

    void DoSomething()
    {
        myConnection.ExecuteCommand();
    }
}

我恭敬地不同意那些认为它不是真正的IoC / DI的人,除非你有一个明确的binder /汇编程序/什么让你将注入的类型与具体的实现连接起来,因为接收依赖的类没有'知道区别。是否在外部类或外部配置文件中安排依赖项是一个实现细节。

答案 1 :(得分:4)

  

那么,DI可以仅用作一种模式而不需要任何额外的框架吗?

是的,绝对。

  

如果是,上面的代码是否是一个例子?

是的,绝对。

  

最后,定义DI模式(如果存在),没有容器的概念。

对象被赋予它所依赖的一切;它的依赖性被注入其中。

答案 2 :(得分:3)

在Java世界中,依赖注入通常涉及框架,容器等。但是没有必要使用所有这些机器。

依赖注入的本质是一个类,它可以被赋予它所依赖的对象,而不是在实现中对它们进行硬编码。如果你可以从类外部“注入”那些“依赖关系”,那么你就有了依赖注入。框架可能是一种很好的方法,但这不是必需的。

在Python世界中,没有依赖注入框架的文化,因此很多程序员都想知道所有的大惊小怪。对于他们来说,DI“只是”能够将对象或类传递给构造函数。

回答您的具体问题:

  1. 是的,DI可以在没有框架的情况下完成。

  2. 是的,上面的代码就是一个例子:PlayerService不知道PlayerClient或PlayerSkinClient来自何处。他们已被注入课堂。请注意,其他人已经回答了这个“否”。

  3. 见上文。

答案 3 :(得分:1)

  1. 是的,虽然与PicoContainer或其他非常小的容器相比,所有这些容器都经过测试和深思熟虑,但你最终会实现同样的功能。结束。 最好的例子是Ayende的"Building an IoC container in 15 lines"

  2. 修改我的回答,我将改为是。我认为这里的主要认知不一致是由于开发人员混淆了许多/大多数DI / IoC框架提供了构建不像new的对象的简洁方法这一事实,这就是他们坚持IoC思想的原因。

  3. 如前所述,Martin Fowler's是规范的定义,在其他地方也有很好的涵盖。

答案 4 :(得分:1)

有趣的是,第一个例子根本不是DI,而是Service Locator anti-pattern的一个例子 - 没有注入

您自己的示例是纯DI - 更具体地说是构造函数注入模式的实现。

DI不是一种模式,而是一系列模式和原则。当我们手动连接所有依赖项时,我们通常将其称为穷人的DI 。换句话说,DI容器是严格可选的,但强烈建议:)

另见this description of what a DI Container brings to the table

答案 5 :(得分:0)

答案 6 :(得分:0)

这真的不是DI的一个例子。

原因是您正在安排依赖项(在您的案例中为PlayerClientPlayerSkinClient),而不是为您安排依赖项(在构造具体的{{1}时由容器传入})。这是“控制反转” - 你有一个被调用的构造函数,而不是调用它。这是使用DI模式的主要原因,并且拥有一个容器,用于对请求它们的对象进行范围,解析和放置,这是执行此操作的具体方法。

当您让容器管理此依赖项的传递时,您不需要编写和维护将依赖项传递给具体IPlayerService的代码。这允许您进行IPlayerServicePlayerClient的其他实现(例如,当您想要模拟它们来构建测试时)而不必更新初始化PlayerSkinClient的代码。这就是DI模式有用的原因。

答案 7 :(得分:0)

以下是其他一些文章,可能会按照“应该先阅读”的顺序带来一些关于这个主题的其他重要细节:

  1. Dependency Injection Demystified;
  2. Dependency injection
  3. 阅读完这些内容之后,我发现自己对这个主题有了更好的理解。

相关问题