依赖注入容器与注册表模式

9
我理解依赖注入原则的主要目的是为了解耦代码。在类中,而不是使用“new”实例化对象,将对象注入进去,达到松散耦合。
现在如果我需要传递一组对象,在整个应用程序的多个类中使用,我可以创建一个容器(通常称为依赖注入容器)。
这正是我正在做的事情,因为我必须传递配置对象、记录器对象、翻译器对象等,它们将被应用程序中多个类的实例使用。我通过多个类传递整个容器,即使并非所有类都需要访问容器中的所有对象。这引出了以下问题:如果我创建一个全局注册表,并将对象放入其中,然后像Registry::getInstance()->get('logger')这样检索它们,那么有什么区别呢?无论我使用全局注册表还是依赖容器,类实例都可以访问容器或注册表中的所有对象,即使它们不需要看到或访问其中的所有内容。
结论:如果我在类之间传递依赖注入容器或全局注册表有什么区别?

据我所知,如果您将容器注入类中,则这是服务定位而不是依赖项注入。 - Orangepill
那么什么是依赖注入容器? - Matthew
1
据我理解,依赖注入总是将依赖项从类外部引入,而服务定位器可以在任何地方发生。服务定位器的不好之处在于它们隐藏了对象真正的依赖关系。 - Orangepill
顺便提一下,记录器(logger)永远不应该成为类的依赖项。相反,它应该是一个装饰器(decoration)。 - tereško
1
你所说的一切似乎更接近于服务定位器模式... - MattDavey
3个回答

7

注意: 有时人们会用不同的名称来称呼注册表。我见过的常见名称有:定位器、上下文和系统。

使用全局注册表会使您的代码与该注册表的类名绑定在一起。这意味着您无法独立测试您的代码。而且,注册表设置的方式是欺骗性的:您永远无法知道将需要哪个实例。

人们经常将DI容器与注册表混淆。

DI容器不是您注入到类中的东西。相反,它是工厂的增强功能:它确定类Foo需要将Bar实例注入构造函数。然后,根据设置,它要么获取Bar的新实例,要么使用现有实例,以提供所需的依赖项。

  • 在简单工厂中,您通常必须硬编码将注入的依赖关系。在这种情况下,工厂可以构建的内容受限于类构造函数的“印记”。

  • 当工厂使用作为DI容器时,它可以生成具有各种依赖关系的实例。在这种设置中,您的工厂专注于构建与某些特定命名空间(或其他高级别)逻辑组的类相关联的实例。


所有依赖注入容器都可以被(误)用作服务定位器这个说法是否正确?而这两种模式之间的巨大区别更多地在于如何使用它们,以及它们的(容器/定位器)实现方式。 - Orangepill
@tereško,阅读您的答案,我应该理解注册表(或服务定位器)允许访问对象,而DIC更像是一个知道如何创建这些对象的工厂?那么将这两种模式进行比较就没有意义了,DIC不是注册表的替代品?虽然似乎您建议避免使用注册表,而选择另一种解决方案? - Matthew
我认为一个完全实现的DIC系统会使用一个非静态、非全局的注册表作为初始化实例的缓存。注册表是关于存储对象的。DI容器是关于发现一个类创建实例所需的要求。无论DI容器本身创建实例还是将其留给工厂更多地是一个偏好问题。 - tereško

6
我认为这里缺少的是CompositionRoot。根据DI原则,一旦在组合根中定义了绑定,就可以在整个应用程序中使用该引用。这就是DI容器带来的价值。
按照定义,“组合根是应用程序中(最好是)唯一的位置,其中模块被组合在一起。”
我会补充说,组合根也是所有关于应用程序行为的重要决策都发生的唯一位置。这就是何时在什么情况下使用对象。这是OO系统的核心,其中对象之间的交互提供了系统的行为。

如果我理解正确的话,在基于PHP MVC的应用程序中,组合根将是引导程序或靠近引导程序的类。在那里,我将构建我的类并注入它们的依赖项。但这让我有一个问题:在这种情况下,我无法动态构建我的组件,因为所有依赖关系都必须在应用程序入口点设置。 - Matthew
2
这篇文章回答了我上次的评论:http://richardmiller.co.uk/2011/07/07/dependency-injection-moving-from-basics-to-container/ - Matthew

0

我同意teresko的大部分观点,并想补充一些要点:

  • 如果您觉得您的服务被多个不同的实例共享,那么您可以将它们注册为容器中的单例,但是如果这对您来说是一个问题,那么您就必须考虑多线程。
  • 每当您在依赖注入场景中感到无助时,请记住工厂(抽象工厂模式)几乎总是解决方案。
  • 您提到的服务似乎是横切关注点,因此我会使用AOP技术来避免用这些关注点膨胀我的代码库,这将使代码更加清晰。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接