gpt4 book ai didi

design-patterns - ServiceLocator 是反模式吗?

转载 作者:行者123 更新时间:2023-12-03 04:31:09 25 4
gpt4 key购买 nike

最近看了Mark Seemann's article关于服务定位器反模式。
作者指出了 ServiceLocator 是反模式的两个主要原因:

  • API 使用问题 (我对此非常满意)
    当类使用服务定位器时,很难看到它的依赖关系,因为在大多数情况下,类只有一个 PARAMETERLESS 构造函数。
    与 ServiceLocator 相比,DI 方法通过构造函数的参数显式公开依赖关系,因此在 IntelliSense 中很容易看到依赖关系。
  • 维护问题 (这让我很困惑)
    考虑以下示例

  • 我们有一个使用服务定位器方法的“MyType”类:

    public class MyType
    {
    public void MyMethod()
    {
    var dep1 = Locator.Resolve<IDep1>();
    dep1.DoSomething();
    }
    }
    现在我们要向类“MyType”添加另一个依赖项
    public class MyType
    {
    public void MyMethod()
    {
    var dep1 = Locator.Resolve<IDep1>();
    dep1.DoSomething();

    // new dependency
    var dep2 = Locator.Resolve<IDep2>();
    dep2.DoSomething();
    }
    }
    这就是我的误解开始的地方。作者说:

    It becomes a lot harder to tell whether you are introducing a breaking change or not. You need to understand the entire application in which the Service Locator is being used, and the compiler is not going to help you.


    但是等一下,如果我们使用 DI 方法,我们会在构造函数中引入一个带有另一个参数的依赖项(在构造函数注入(inject)的情况下)。而且问题仍然存在。如果我们可能忘记设置 ServiceLocator,那么我们可能会忘记在 IoC 容器中添加新的映射,而 DI 方法也会有同样的运行时问题。
    另外,作者提到了单元测试的困难。但是,我们不会对 DI 方法有问题吗?我们不需要更新所有实例化该类的测试吗?我们将更新它们以传递一个新的模拟依赖项,以使我们的测试可编译。而且我没有看到该更新和时间花费有任何好处。
    我并不是要为服务定位器方法辩护。但是这种误解让我认为我正在失去一些非常重要的东西。有人可以打消我的疑虑吗?
    更新(摘要):
    我的问题“服务定位器是否是反模式”的答案实际上取决于具体情况。而且我绝对不建议将其从您的工具列表中删除。当您开始处理遗留代码时,它可能会变得非常方便。如果您有幸处于项目的一开始,那么 DI 方法可能是更好的选择,因为它比 Service Locator 具有一些优势。
    以下是说服我在新项目中不使用 Service Locator 的主要区别:
  • 最明显和最重要的:服务定位器隐藏类依赖
  • 如果您正在使用某些 IoC 容器,它可能会在启动时扫描所有构造函数以验证所有依赖项,并在缺少映射(或错误配置)时立即向您提供反馈;如果您将 IoC 容器用作服务定位器
  • ,这是不可能的

    有关详细信息,请阅读下面给出的出色答案。

    最佳答案

    如果您将模式定义为反模式只是因为在某些情况下它不适合,那么是的,它是一种反模式。但是根据这种推理,所有模式也都是反模式。
    相反,我们必须查看模式是否有效,对于服务定位器,有几个用例。但是,让我们从您给出的示例开始。

    public class MyType
    {
    public void MyMethod()
    {
    var dep1 = Locator.Resolve<IDep1>();
    dep1.DoSomething();

    // new dependency
    var dep2 = Locator.Resolve<IDep2>();
    dep2.DoSomething();
    }
    }
    该类的维护噩梦是隐藏了依赖项。如果您创建并使用该类:
    var myType = new MyType();
    myType.MyMethod();
    如果使用服务位置隐藏它们,您将不了解它具有依赖关系。现在,如果我们改为使用依赖注入(inject):
    public class MyType
    {
    public MyType(IDep1 dep1, IDep2 dep2)
    {
    }

    public void MyMethod()
    {
    dep1.DoSomething();

    // new dependency
    dep2.DoSomething();
    }
    }
    您可以直接发现依赖关系,并且在满足它们之前不能使用这些类。
    由于这个原因,在典型的业务应用程序中,您应该避免使用服务位置。当没有其他选项时,它应该是使用的模式。
    模式是反模式吗?
    不。
    例如,如果没有服务位置,控制容器的反转将无法工作。这就是他们在内部解决服务的方式。
    但更好的例子是 ASP.NET MVC 和 WebApi。你认为是什么让 Controller 中的依赖注入(inject)成为可能?没错——服务地点。
    你的问题

    But wait a second, if we were using DI approach, we would introduce adependency with another parameter in constructor (in case ofconstructor injection). And the problem will be still there.


    还有两个更严重的问题:
  • 使用服务位置,您还添加了另一个依赖项:服务定位器。
  • 您如何判断依赖项应该具有哪个生命周期,以及应该如何/何时清理它们?

  • 通过使用容器的构造函数注入(inject),您可以免费获得它。

    If we mayforget to setup ServiceLocator, then we may forget to add a newmapping in our IoC container and DI approach would have the samerun-time problem.


    确实如此。但是使用构造函数注入(inject),您不必扫描整个类来找出缺少哪些依赖项。
    一些更好的容器还在启动时验证所有依赖项(通过扫描所有构造函数)。因此,使用这些容器,您会直接得到运行时错误,而不是在稍后的某个时间点。

    Also, author mentioned about unit test difficulties. But, won't we have issues with DI approach?


    不,因为您不依赖于静态服务定位器。您是否尝试过使用静态依赖项进行并行测试?这不好玩。

    关于design-patterns - ServiceLocator 是反模式吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22795459/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com