【问题标题】:Why would you want Dependency Injection without configuration?为什么要在没有配置的情况下进行依赖注入?
【发布时间】:2009-02-03 08:34:42
【问题描述】:

在阅读了 this question 的精彩答案后,我观看了 Justin Etheredge 的截屏视频。一切看起来都很好,只需最少的设置,您就可以直接从代码中获得 DI。

现在困扰我的问题是:为什么要使用不使用配置文件的 DI 框架?这不是使用 DI 基础架构的全部意义,以便您可以在构建/发布/任何代码之后更改行为(可以说是“策略”)?

谁能给我一个很好的用例来验证使用像 Ninject 这样的非配置 DI 吗?

【问题讨论】:

    标签: configuration dependency-injection inversion-of-control


    【解决方案1】:

    我不认为你想要一个 DI 框架没有配置。我认为你想要一个具有你需要的配置的 DI 框架。

    我以春天为例。回到“旧时代”,我们过去常常将所有内容都放在 XML 文件中以使所有内容都可配置。

    当切换到完全注释的机制时,您基本上定义了您的应用程序包含的组件角色。所以一个给定的 例如,服务可能有一个用于“常规运行时”的实现,其中有另一个实现属于 在应用程序的“存根”版本中。此外,在为集成测试进行接线时,您可能正在使用第三种实现。

    以这种方式看待问题时,您很快就会意识到大多数应用程序只包含非常有限的组件角色集 在运行时 - 这些是实际导致使用不同版本的组件的事情。通常,组件的给定实现总是绑定到这个角色;这确实是该实现存在的原因。

    因此,如果您让“配置”简单地指定您需要哪些组件角色,那么您无需更多配置即可摆脱困境。 当然,总会有例外,但你只需要处理这些例外。

    【讨论】:

    • 但是根本没有配置怎么办(就像在 google guice 中一样) - 你如何指定“组件角色”?
    • 上次我查看 guice 时,它​​缺少通过混合注释、xml 和角色可以获得的“约定优于配置”方面,这也是我不使用 spring javaconfig 的原因。我真的更喜欢混合范式。用做同样事情的代码替换 xml 对我来说并没有减少它,也许只是我心胸狭窄,但我看不到基于代码的配置如何为我增加价值。但我不知道你是否可以在 guice 中实现基于约定的设置。
    【解决方案2】:

    我在使用 krosenvold 的道路上,在这里,只有更少的文本:在大多数应用程序中,每个所需的“服务”都有一个实现。我们根本不会编写每个对象需要每个服务的 10 个或更多实现的应用程序。所以用一种简单的方式说“这是默认实现,99% 使用此服务的对象都会对此感到满意”是有意义的。

    在测试中,您通常使用特定的模型,因此也不需要任何配置(因为您手动进行接线)。

    这就是约定优于配置的意义所在。大多数时候,配置只是重复 DI 框架应该已经知道的东西的转储:)

    在我的应用程序中,我使用类对象作为查找实现的键,而“键”恰好是默认实现。如果我的 DI 框架在配置中找不到覆盖,它只会尝试实例化密钥。拥有超过 1000 个“服务”,我需要四个覆盖。那将是很多无用的 XML 编写。

    【讨论】:

      【解决方案3】:

      通过依赖注入,单元测试的设置变得非常简单,因为您可以在被测对象中注入模拟而不是真实对象。您不需要为此进行配置,只需在单元测试代码中创建并注入模拟即可。

      【讨论】:

      • 不仅是单元测试 - 这是部署测试/开发版应用程序的好方法,其中包含您不想测试(或向客户展示)的部分的模拟组件。
      • 对,但我为什么需要 Ninject 呢?
      【解决方案4】:

      我在我的博客上收到了这条评论,来自Nate Kohari

      很高兴您正在考虑使用 Ninject! Ninject 的立场是 您的 DI 框架的配置是 实际上是您的应用程序的一部分,并且 不应该是公开可配置的。如果 你希望某些绑定是 可配置,您可以轻松地使您的 Ninject 模块读取您的 app.config。 在代码中绑定可以节省您的时间 从 XML 的冗长,并给出 您的类型安全性、可重构性和 智能感知。

      【讨论】:

        【解决方案5】:

        您甚至不需要使用 DI 框架来应用依赖注入模式。如果除了重新编译代码之外不需要可配置性,您可以简单地使用静态工厂方法来创建对象。

        所以这完全取决于您希望应用程序的可配置性。如果您希望它在无需重新编译代码的情况下可配置/可插入,您将需要可以通过文本或 xml 文件配置的东西。

        【讨论】:

          【解决方案6】:

          我将支持使用 DI 进行测试。我现在只真正考虑使用 DI 进行测试,因为我们的应用程序不需要任何基于配置的灵活性 - 目前它也太大了,无法考虑。

          DI 往往会带来更简洁、更独立的设计 - 这会带来全方位的优势。

          【讨论】:

            【解决方案7】:

            如果您想在发布构建后更改行为,那么您将需要一个支持外部配置的 DI 框架,是的。

            但我可以想到其他不需要此配置的场景:例如控制业务逻辑中组件的注入。或者使用 DI 框架来简化单元测试。

            【讨论】:

              【解决方案8】:

              您应该阅读PRISM in .NET(在 .NET 中执行复合应用程序的最佳实践)。在这些最佳实践中,每个模块在共享容器内“公开”它们的实现类型。这样每个模块对“谁提供这个接口的实现”都有明确的责任。我认为当您了解 PRISM 的工作原理时就足够清楚了。

              【讨论】:

                【解决方案9】:

                当您使用控制反转时,您正在帮助您的班级尽可能少地做事。假设您有一些 Windows 服务等待文件,然后对文件执行一系列进程。其中一个过程是将其转换为 ZIP,然后通过电子邮件发送。

                public class ZipProcessor : IFileProcessor
                {
                  IZipService ZipService;
                  IEmailService EmailService;
                
                  public void Process(string fileName)
                  {
                    ZipService.Zip(fileName, Path.ChangeFileExtension(fileName, ".zip"));
                    EmailService.SendEmailTo(................);
                  }
                }
                

                当您可以有专门的课程为您完成压缩和发送电子邮件时,为什么这个课程需要实际进行压缩和发送电子邮件?显然你不会,但这只是我的观点:-)

                除了不实现 Zip 和 email 之外,为什么类应该知道哪个类实现了服务?如果您将接口传递给此处理器的构造函数,则它永远不需要创建特定类的实例,它会获得完成这项工作所需的一切。

                使用 D.I.C.您可以配置哪些类实现某些接口,然后让它为您创建一个实例,它将依赖项注入到类中。

                var processor = Container.Resolve<ZipProcessor>();
                

                因此,现在您不仅将类的功能与共享功能明确地分开,而且还阻止了消费者/提供者对彼此有任何明确的了解。这使得阅读代码更容易理解,因为同时需要考虑的因素更少。

                最后,在进行单元测试时,您可以传入模拟的依赖项。当您测试 ZipProcessor 时,您的模拟服务只会断言该类尝试发送电子邮件,而不是真正尝试发送电子邮件。

                //Mock the ZIP
                var mockZipService = MockRepository.GenerateMock<IZipService>();
                mockZipService.Expect(x => x.Zip("Hello.xml", "Hello.zip"));
                
                //Mock the email send
                var mockEmailService = MockRepository.GenerateMock<IEmailService>();
                mockEmailService.Expect(x => x.SendEmailTo(.................);
                
                //Test the processor
                var testSubject = new ZipProcessor(mockZipService, mockEmailService);
                testSubject.Process("Hello.xml");
                
                //Assert it used the services in the correct way
                mockZipService.VerifyAlLExpectations();
                mockEmailService.VerifyAllExceptions();
                

                简而言之。你会想要这样做 01:防止消费者明确知道哪个提供者实现了它需要的服务,这意味着当你阅读代码时,一次理解的内容更少。 02:使单元测试更容易。

                皮特

                【讨论】:

                • 这是一个很酷的答案,但不完全是我问题的答案。我相当了解 IoC 是什么以及 DI 框架的作用;我只是看不到为什么在代码中也进行配置本身的原因。
                猜你喜欢
                • 1970-01-01
                • 2020-01-05
                • 2020-12-08
                • 2018-07-12
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2013-03-05
                • 1970-01-01
                相关资源
                最近更新 更多