【问题标题】:Dependency injection - trying to avoid using a service locator依赖注入 - 试图避免使用服务定位器
【发布时间】:2017-10-18 08:57:17
【问题描述】:

按照我阅读的指南: https://www.devtrends.co.uk/blog/how-not-to-do-dependency-injection-the-static-or-singleton-container

我想尽量避免使用服务定位器。 但另一方面,我没有在 startup.cs 文件中注册所有类型。我认为所有这些内部类型都在主startup.cs中引用是不对的

我目前有一个工厂类,其中包含一组构建器类。 每个构建器类负责创建一个特定的对象。 我不想提前创建所有这些构建器类,因为我可能不需要使用它们并且创建它们有点繁重。 我在上面的链接中看到了如何实现此目的的示例。然而,startup.cs 类需要了解所有这些构建器。我认为这不合适,我宁愿让工厂类成为唯一暴露给它们的类。我试图了解是否有某种 func/action 方法可以从 startup.cs 文件注入到我的工厂类中。这个函数/动作将负责创建/注册构建器,然后我可以在类工厂中激活这个函数/动作。我希望这个 func/action 接收构建器的接口/类/可能名称,但使用泛型不起作用。我搜索了很多并没有找到任何解决方案,所以我认为这是不可能的。 似乎我有两个选择: 1.使用服务定位器。这样只有工厂类会知道建设者。但是,如果将来,如果我想更改 DI,我需要“触摸”工厂类(我正在污染工厂类)。希望所有 DI 代码仅位于 startup.cs 类中。 2. 在 startup.cs 中注册构建器,但现在 startup.cs 知道构建器。这有点耦合代码,而不是真正的单一职责角色

如果可以从 startup.cs 中为工厂类注入一个 func/action 来进行注册,但工厂类本身会激活它,那就太好了。 这可能吗?

【问题讨论】:

  • 在单个类中注册类型没有错。如果需要,可以将其移动到配置文件中。
  • 好吧,我不喜欢配置文件,通常尽量避免使用它们。在 startup.cs 中注册所有类型 - 这似乎不太健康。如果我们想要更改构建器,我们需要“离开”工厂/构建器区域并跳转到 startup.cs 并开始删除/添加注册。我认为这是注册Logger/Configuration/等通用组件的好地方。但不会将我所有的类型都加载到一个地方。
  • 你不会每小时都更改旧的依赖项,所以我不明白只去一个文件并更改一行代码的问题是什么。

标签: dependency-injection


【解决方案1】:

我想尽量避免使用服务定位器

太好了,因为服务定位器is an anti-patttern

不要在 startup.cs 文件中注册所有类型。

您应该在应用程序的一个“区域”进行注册:启动路径。这个区域通常被称为Composition Root(组成对象图的地方)。

我认为所有这些内部类型都在主startup.cs中引用是不对的

无论您如何设计,启动程序集都是系统中最不稳定的部分,它总是依赖于应用程序中的所有其他程序集。直接或传递(通过另一个引用的程序集)。依赖注入的整个想法是最小化组件之间的耦合,而做到这一点的方法是通过将耦合移动到组合根来集中耦合。但是,通过将类型设置为内部,您正在分散对象组合,这限制了您的灵活性。例如,为那些已注册的类型应用装饰器或拦截器并在全局范围内控制它们变得更加困难。阅读this question and its two top voted answers了解更多信息。

我没有注册所有类型

组合根太大的担忧是无效的。人们可以轻松地将组合根拆分为多个较小的函数或类,这些函数或类都驻留在启动程序集中。最重要的是,如果您阅读this,您会明白显式注册所有类型(也称为“显式注册”)通常是没有意义的。在这种情况下,您最好在没有容器的情况下使用 DI(又名Pure DI)。所有类型都显式注册的组合根不是很容易维护。 DI Container 变得强大的领域之一是通过其批量注册设施。他们使用反射在几行代码中加载和注册一整套类型。添加新类型不会导致您的 Composition Root 发生变化,从而为您提供最高的可维护性。

我不想提前创建所有这些构建器类,因为我可能不需要使用它们并且创建它们有点繁重

创建实例永远不应该繁重。你的injection constructors should be simple 和组成对象图should be reliable。这使得构建最大的对象图变得非常快。工厂应该减少到an absolute minimum

TLDR;

  • 仅在组合根中注册或组合您的对象图。
  • 避免使用服务定位器反模式;整个应用程序可以(并且应该)完全使用构造函数注入来构建。
  • 使注入构造函数变得简单,并防止它们执行任何其他操作,而不是存储传入的依赖项。
  • 不要使用工厂来组合服务,大多数情况下不需要它们。

【讨论】:

  • 我不认为“服务定位器是一种反模式”。这是一种非常有用的模式,可以解决源代码中一些非常罕见的点内的动态分辨率。滥用任何模式都是臭的——但这不是模式的错。
  • @Console:服务定位器一种反模式,现在很少讨论。但是您可能会对什么是服务定位器以及什么不是服务定位器感到困惑。这是一个good article,它解释了不是每次使用Resolve 方法都是一个服务定位器,它取决于它在系统中的角色。
  • 好吧,如果你这样说 - 它符合你所说的;)。 architectural code that uses the mechanism of service location 的东西我需要另一个名字而不是
  • 只要在内部组合根中使用,它就不是服务定位器的实现。如果在组合根外部查询服务,则它服务定位器反模式的实现。
猜你喜欢
  • 2016-08-06
  • 2023-02-17
  • 1970-01-01
  • 2013-01-21
  • 1970-01-01
  • 1970-01-01
  • 2012-08-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多