【问题标题】:Default constructor vs IOC container默认构造函数与 IOC 容器
【发布时间】:2010-09-11 04:25:10
【问题描述】:

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

也就是说,这段代码有什么问题?

public class MyClass
{
    private IMyInterface _myInterface;

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

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

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

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

【问题讨论】:

    标签: inversion-of-control


    【解决方案1】:

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

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

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

    【讨论】:

      【解决方案2】:

      选择一边:)

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

      【讨论】:

        【解决方案3】:

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

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

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

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

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

        【讨论】:

          【解决方案4】:

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

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

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

          【讨论】:

            【解决方案5】:

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

            【讨论】:

              【解决方案6】:

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

              【讨论】:

                【解决方案7】:

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

                还是我错过了什么?

                【讨论】:

                  【解决方案8】:

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

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

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 2011-04-12
                    • 2013-03-16
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2016-07-23
                    相关资源
                    最近更新 更多