12

有人可以向我解释使用 IOC 容器相对于简单地将默认实现硬编码到默认构造函数中的优势吗?

换句话说,这段代码有什么问题?

public class MyClass
{
    private IMyInterface _myInterface;

    public MyClass()
    {
        _myInterface = new DefaultMyInterface();
    }

    public MyClass(IMyInterface myInterface)
    {
        _myInterface = myInterface;
    }
}

据我所知,这个类足够支持构造函数注入,因此单元测试和模拟很容易完成。除此之外,默认构造函数消除了 IOC 容器的计算开销(更不用说整个过程更加透明)。

我可以看到使用 IOC 容器的唯一好处是如果您需要经常切换接口的实现。我错过了什么吗?

4

8 回答 8

12

IoC 的想法是将组件的部分功能委托给系统的另一部分。在 IoC 世界中,您拥有彼此不了解的组件。您的示例违反了这一点,因为您正在 MyClass 和 IMyInterface 的某些实现之间创建紧密耦合。主要思想是您的组件不知道如何使用它。在您的示例中,您的组件对其使用做出了一些假设

实际上这种方法可以工作,但是混合 IoC 和显式对象初始化并不是 IMO 的好习惯。

IoC 通过执行后期绑定来为您提供松散耦合,但代价是代码清晰。当您向此过程添加其他行为时,它会使事情变得更加复杂,并且当某些组件可能接收到具有不需要或不可预测行为的对象时可能会导致错误。

于 2008-09-19T10:22:59.857 回答
4

选择一方:)

简而言之,推荐国际奥委会。代码的问题是,如果不重新编译代码,我就无法换出依赖项的默认实现,正如您最后所说的那样。IOC 允许您在外部文件中更改对象的配置或组成,而无需重新编译。
IOC 从代码的其余部分接管“构造和组装”责任。 IOC 的目的不是让您的代码可测试……这是一个令人愉快的副作用。(就像 TDDed 代码带来更好的设计一样)

于 2008-09-19T10:36:58.873 回答
2

这段代码没有任何问题,您仍然可以将它与 Spring 和 Guice 等依赖注入框架一起使用。

许多开发人员将 Spring 的 XML 配置文件视为优于在代码中连接依赖项的优势,因为您可以在不需要编译步骤的情况下切换实现。这种好处实际上是在您已经在类路径中编译了多个实现并且您希望在部署时选择实现的情况下实现的。您可以想象一个组件在部署后由第三方提供的情况。类似地,您可能希望在部署后将其他实现作为补丁发布。

但并非所有 DI 框架都使用 XML 配置。例如,Google Guice 将模块编写为 Java 类,必须像任何其他 Java 类一样进行编译。

那么如果你甚至需要一个编译步骤,那么 DI 有什么优势呢?这使我们回到您最初的问题。我可以看到以下优点:

  1. 整个应用程序中 DI 的标准方法。
  2. 配置与其他逻辑巧妙地分离。
  3. 注入代理的能力。例如,Spring 允许您通过注入代理而不是您的实现来进行声明性事务处理
  4. 更容易重用配置逻辑。当您广泛使用 DI 时,您会看到一个复杂的依赖关系树随着时间的推移而演变。在没有明确分离的配置层和框架支持的情况下管理它可能是一场噩梦。DI 框架通过继承和其他方式使重用配置逻辑变得容易。
于 2008-09-19T10:50:31.337 回答
2

我唯一担心的是(1)您的默认服务依赖项本身是否具有另一个/辅助服务依赖项(依此类推......您的 DefaultMyInterface 依赖于 ILogger)和(2)您需要将第一个服务依赖项与第二个服务依赖项(需要使用存根 ILogger 测试 DefaultMyInterface)。在这种情况下,您显然需要丢失默认的“new DefaultMyInterface”,而是执行以下操作之一:(1) 纯依赖注入或 (2) 服务定位器或 (3) container.BuildUp(new DefaultMyInterface());

列出的其他海报中的一些问题可能对您的问题不公平。您不是在询问多个“生产”实现。您在询问单元测试。 在单元测试你的方法的情况下,我的第一个警告是,似乎是合法的;我也会考虑在简单的单元测试用例中使用它。

同样,一些响应者对重复性表示担忧。我也不喜欢重复,但是如果(1)您的默认实现确实是您的默认实现(YAGNI:您没有更改默认设置的计划)并且(2)您不相信我所说的第一个警告适用并且(3) 更喜欢您共享的更简单的代码方法,那么我认为这种特殊形式的重复不是问题。

于 2010-04-13T17:52:24.090 回答
1

除了松耦合之外,IoC 还可以减少代码重复。当您使用 IoC 并且想要更改接口的默认实现时,您只需在一处进行更改。当您使用默认构造函数注入默认实现时,您必须在使用接口的任何地方更改它。

于 2008-09-19T10:37:45.097 回答
1

除了其他评论之外,在这些情况下,人们可能会争论 DRY(不要重复自己)原则。必须将默认构造代码放在每个类中是多余的。它还介绍了不需要的特殊情况处理。

于 2008-10-22T17:52:10.170 回答
0

我不明白为什么您对默认实现进行硬编码的技术不能与 IOC 容器一起使用。只是,您未在配置中指定的依赖项将采用默认实现。

还是我错过了什么?

于 2008-09-19T10:23:07.627 回答
0

您可能想要使用 IOC 容器的一个原因是便于后期配置您的软件。

例如,假设您提供了特定接口的多个实现——客户(或您的专业服务团队)可以通过修改 IOC 配置文件来决定使用哪一个。

于 2008-09-19T10:24:35.097 回答