ServiceLocator 是一种反模式吗? [关闭]

2024-01-18

最近我读过马克·西曼的文章 http://blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern/关于服务定位器反模式。

作者指出了 ServiceLocator 是反模式的两个主要原因:

  1. API使用问题(我对此非常满意)
    当类使用服务定位器时,很难看到它的依赖关系,因为在大多数情况下,类只有一个无参数构造函数。 与 ServiceLocator 相比,DI 方法通过构造函数的参数显式公开依赖关系,因此可以在 IntelliSense 中轻松查看依赖关系。

  2. 维修问题(这让我很困惑)
    考虑下面的例子

我们有一个班'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();
    }
}

这就是我的误解开始的地方。作者说:

判断您是否正在引入重大变更变得更加困难。您需要了解使用服务定位器的整个应用程序,编译器不会为您提供帮助。

但是等一下,如果我们使用 DI 方法,我们将在构造函数中引入与另一个参数的依赖关系(在构造函数注入的情况下)。问题仍然存在。如果我们可能忘记设置 ServiceLocator,那么我们可能会忘记在 IoC 容器中添加新映射,并且 DI 方法也会出现相同的运行时问题。

另外,作者提到了单元测试的困难。但是,DI 方法不会有问题吗?我们不需要更新实例化该类的所有测试吗?我们将更新它们以传递新的模拟依赖项,以使我们的测试可编译。而且我认为更新和花费时间没有任何好处。

我并不是想捍卫服务定位器方法。但这种误解让我觉得我失去了一些非常重要的东西。有人能打消我的疑虑吗?

更新(摘要):

我的问题“服务定位器是反模式吗”的答案实际上取决于具体情况。我绝对不会建议将其从您的工具列表中删除。当您开始处理遗留代码时,它可能会变得非常方便。如果您足够幸运,处于项目的最开始阶段,那么 DI 方法可能是更好的选择,因为它比服务定位器具有一些优势。

以下是一些主要差异,这些差异说服了我不在我的新项目中使用服务定位器:

  • 最明显和最重要的是:服务定位器隐藏了类依赖性
  • 如果您正在使用某些 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();

如果使用服务位置隐藏它们,您不明白它具有依赖关系。现在,如果我们改用依赖注入:

public class MyType
{
    public MyType(IDep1 dep1, IDep2 dep2)
    {
    }

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

        // new dependency
        dep2.DoSomething();
    }
}

您可以直接发现依赖关系,并且在满足它们之前不能使用这些类。

在典型的业务应用程序中,您应该避免使用服务位置。它应该是没有其他选择时使用的模式。

该模式是反模式吗?

No.

例如,如果没有服务位置,控制容器的反转将无法工作。这就是他们在内部解决服务的方式。

但更好的例子是 ASP.NET MVC 和 WebApi。您认为是什么使得控制器中的依赖注入成为可能?没错——服务地点。

你的问题

但是等一下,如果我们使用 DI 方法,我们会引入一个 与构造函数中另一个参数的依赖关系(如果是 构造函数注入)。问题仍然存在。

还有两个更严重的问题:

  1. 通过服务定位,您还可以添加另一个依赖项:服务定位器。
  2. 您如何判断依赖项应具有哪个生命周期,以及应如何/何时清理它们?

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

如果我们可以 忘记设置 ServiceLocator,那么我们可能会忘记添加新的 我们的 IoC 容器和 DI 方法中的映射将具有相同的 运行时问题。

这是真的。但通过构造函数注入,您不必扫描整个类来找出缺少哪些依赖项。

一些更好的容器还会在启动时验证所有依赖项(通过扫描所有构造函数)。因此,使用这些容器,您会直接收到运行时错误,而不是在稍后的某个时间点。

另外,作者提到了单元测试的困难。但是,DI 方法不会有问题吗?

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

ServiceLocator 是一种反模式吗? [关闭] 的相关文章

  • 将 javascript 放在 header 之外有多糟糕?

    这个问题几乎已经说明了一切 我开始添加一些功能到我的周末项目 http www my clock net 对于我和几个朋友来说 这是一个小应用程序 因为我们是交换生 所以它对我们来说有点有用 但事情是这样的 我在 php 中执行此操作并使用
  • 如何注入“运行时”依赖项,例如登录用户,该依赖项在应用程序启动时不可用?

    我只是不明白这个 我在我的 java GWT 应用程序中使用 Gin 来进行 DI 登录屏幕集成到完整的应用程序窗口中 用户登录后 我想将用户对象注入到我创建的其他类 例如 GUI Presenters 中 因此我相信存在某种运行时依赖性
  • 存储库模式和 Linq to sql

    我正在尝试使用角色表 用户表和具有用户 ID 角色 ID 的外部参照表来实现用户身份验证和授权 为了实现通用存储库来更新角色 插入角色 添加用户 将用户添加到角色 更新用户 更新用户角色 验证用户 添加用户会话进行审核等 我是否需要为每个存
  • 在 PHP 中扩展单例

    我正在使用一个 Web 应用程序框架 其中一部分由许多服务组成 所有服务均以单例形式实现 它们都扩展了一个 Service 类 其中实现了单例行为 如下所示 class Service protected static instance p
  • Xamarin、Autofac、NavigationService 和 BeginLifetimeScope

    关于带有 autofac 的生命周期范围以及何时在 xamarin 应用程序中使用它们的初学者问题 正如这篇文章中提到的 https nblumhardt com 2011 01 an autofac lifetime primer htt
  • F#:模式构成?

    我正在尝试编写一个由另外两个模式组成的模式 但我不确定如何去做 我的输入是字符串列表 文档 我有一个与文档标题匹配的模式和一个与文档正文匹配的模式 该模式应该匹配整个文档并返回标题和正文模式的结果 您可以使用以下命令一起运行两个模式 您在问
  • 使用存储库时,ASP.NET MVC 中业务逻辑的最佳位置是什么?

    在 ASP NET MVC 项目中实现数据库存储库时 将业务逻辑放入其中是否正确 或者将逻辑放入控制器类中可能更好 或者使用额外的服务和帮助程序类来操作数据 最终 除了其自己的层 作为 模型 层的一部分 之外 您的业务逻辑没有一个完美的位置
  • 传统 3 层架构与带 IOC 的 3 层架构

    我正在构建一个包含表示层 PL 业务逻辑层 BLL 和数据访问层 DAL 的 3 层架构 传统的 3 层架构逻辑指出 BLL 应充当 PL 和 DAL 之间的中介 PL 甚至不应该知道数据库的存在 而 DAL 不应该知道 BLL 或 PL
  • MVC 3 将实体作为接口传递

    我目前正在开发一个 MVC 3 项目 使用 Ninject 作为我的 DI 业务对象存储在单独的程序集中 我遇到了控制器参数的问题 当回发 CRUD 操作时 我收到错误 无法创建接口实例 我知道您无法创建接口的实例 但似乎解决此问题的唯一方
  • @DependsOn 注释的逆

    Spring 可能还有其他 DI 容器 但我使用的是 Spring 识别 DependsOn 注释 您可以使用它来标识必须在该 bean 之前启动的任何其他 bean 例如 Component DependsOn initiatedFirs
  • 根据维基百科,为什么“call super”被视为反模式? [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 如何将只缓存某些内容的字段添加到ADT?

    我经常需要向 ADT 添加字段 仅记住一些冗余信息 但我还没有完全弄清楚如何又好又高效地做到这一点 说明问题的最好方法是举个例子 假设我们正在使用无类型 lambda 项 type VSym String data Lambda Var V
  • PHP 中的依赖注入

    我一直在研究依赖注入 我是在关注某件事还是完全没有关注 代码是好是坏 依赖注入与否 下面的代码是CMS系统的基础 现在有一个名为 page details 的表 其中存储了所有网页 目录 文件结构 htaccess index php cl
  • 丰富的领域模型和 ORM

    Martin Fowler 认为贫血领域模型是一种反模式 将持久性模型作为域模型进行滚动似乎也严重偏离 因为对象关系阻抗不匹配 http en wikipedia org wiki Object Relational impedance m
  • 使用超类创建构建器时,父类无法返回子类的实例[重复]

    这个问题在这里已经有答案了 如果我使用构建器模式来配置新对象 我可能有两个类 例如Game and HockeyGame 如下所示 当我想创建一个新的HockeyGame 我得到它的构建器并开始调用方法来根据需要配置对象 我遇到的问题显示在
  • 按类型进行弹簧接线比按名称接线要慢很多

    在我的项目中 我试图迁移 Foo foo Foo beanFactory getBean name into Foo foo beanFactory getBean Foo class 好处是显而易见的 类型安全 更少复杂的代码 更少无用的
  • Dropwizard 和 Guice:注入环境

    我目前正在构建一个基于 Dropwizard Guice Jersey 的应用程序 其中数据库访问暂时由 JDBI 处理 我想要实现的是拥有典型的企业架构 其中资源访问服务类访问 DAO 类 而 DAO 类又访问数据库 如果能以正确的 DI
  • 提供通用服务接口最具体实现的依赖注入机制

    我觉得我和标题一起玩了流行语宾果游戏 这是我所要求的一个简洁示例 假设我有一些实体的继承层次结构 class BaseEntity class ChildAEntity BaseEntity class GrandChildAEntity
  • 用于将 cython 中的许多 C++ 类包装到单个共享对象的项目结构

    我在文档 邮件列表和这个问题在这里 https stackoverflow com questions 10300660 cython and distutils 但我想得到一个更直接的答案来解决我的具体情况 我正在通过尝试一点一点地包装我
  • 委托给子组件的模式

    在我正在工作的产品中 非常基本的场景之一是类的序列化 通常 要序列化的类会在其子组件上调用序列化 例如如果有一个类 s t 班级 A B C D 那么A Pack会调用pack B C D 上的函数 由于有很多这样的类 因此必须一遍又一遍地

随机推荐

  • 在控件的模式弹出扩展器上显示消息框

    我在页面内有一个控件 该控件具有以下模式弹出窗口扩展程序
  • Maven 依赖与多模块?

    对 Maven 非常陌生 有人可以向我解释一下使用 Maven 模块与仅将 Maven 项目的依赖项添加到工作区中的另一个 Maven 项目之间的区别吗 你什么时候会使用其中一种而不是另一种 依赖项是一个预先构建的实体 您可以从 Maven
  • 如何禁用 contenteditable div 中的元素选择和调整大小?

    例如 我有以下布局 div span class text block span Name span a href i class small icon remove i a span div 那么 如何禁用它 和这个 当我尝试完全隐藏控件
  • 列的类型为时间戳,没有时区,但表达式的类型为字符

    我正在尝试在 Redshift 上实施 SCD2 时插入记录 但出现错误 目标表的DDL是 CREATE TABLE ditemp ts scd2 test id INT md5 CHAR 32 record id BIGINT IDENT
  • AutoFixture 3 生成的整数是否唯一?

    生成的整数是IFixture Create
  • Knockout 订阅可观察对象

    我有一个对象 model settings FirstName Joe LastName Bloggs 在我的视图模型中 我将设置设置为可观察的 this Settings ko observable ko mapping fromJS m
  • 如果不存在相同的整行,MySQL INSERT

    我有一个包含 10 列的表 我必须从 CSV 文件添加很多很多行 当然 我不能添加两个相同的行 因此我需要一个 SQL 语句 如果整行确实存在 则忽略该命令 仅当所有字段都相同时才必须忽略 INSERT 两行可能有相同的field1 or
  • ActiveRecord:返回对象时隐藏列

    是否有一种开箱即用的方法可以在返回 ActiveRecord 对象时始终隐藏 删除列 例如 User password 使用内置序列化 您可以覆盖as json模型上的方法来传递其他默认选项 class User lt ActiveReco
  • 如何在 Robolectric 中测试选项菜单项的可见性?

    我想断言菜单项的可见性 但是 我的菜单项总是返回 true 我正在使用以下代码来扩充我的菜单 SherlockMenuInflater inflater new SherlockMenuInflater activity MenuBuild
  • simplecursoradapter textview 给出 nullpointerException

    我有两个 xml 文件 一个是列表视图 另一个是列表视图和一些 texview 的布局 我想更改第二个 xml 文件中文本视图的颜色 这就是我到目前为止所做的 main1 xml
  • Laravel Eloquent `take` 和 `orderBy`

    当我尝试使用每个 take 和 orderBy 查询时 模型返回一些记录 this gt hasMany App User gt take 3 this gt hasMany App User gt orderBy id desc 但是当我
  • 如何在CSS中制作具有透明度的径向渐变

    我想在透明度变化的地方制作一个径向渐变 我可以让它线性工作 但不是径向工作 background webkit gradient linear left top left bottom from rgba 50 50 50 0 8 to r
  • 具有高多边形网格的 OpenGL 3D 光线拾取

    如何在包含高多边形网格的模型的 3D 场景中实现 3D 光线拾取 迭代所有三角形来执行三角形线相交测试需要花费太多时间 我知道存在八叉树等方法 并且应该可以将这些方法用于场景中的模型 但我不知道应该如何在网格级别使用这些概念 但是 如果您在
  • GWT - 如何编译移动排列

    我知道如何使用延迟绑定为不同的用户代理编译 GWT 应用程序 但这似乎没有提供区分桌面 移动浏览器的方法 除了制作基于 gwt mobile webkit 的新应用程序之外 如何将现有的 GWT 应用程序转换为具有重新设计的移动界面 如果您
  • 如何使maven-compiler-plugin不隐藏错误源位置

    也许有一个maven compiler plugin这个选项 但我还没有找到 When javac直接运行并打印错误 在消息的第一行之后 它显示受影响的源行下一行上有一个插入符号指向错误位置 它看起来像这样 com invariantpro
  • 确保泛型类型在 Typescript 中仅具有原始属性

    我有一个采用泛型类型的函数 我需要确保该类型是 JSON 可序列化的 也称为仅原始属性 我的尝试是为 JSON 兼容类型定义一个接口 并强制我的泛型扩展此类型 type JSONPrimitive string number boolean
  • 如何通过按键终止 while 循环?

    我正在读取串行数据并使用 while 循环写入 csv 文件 我希望用户一旦觉得已经收集了足够的数据就能够终止 while 循环 while True do a bunch of serial stuff if the user press
  • swift 中 DispatchQueue 类型之间的区别

    据我了解 Swift 中有 3 种类型的 DispatchQueue 主要 连续剧 主线 全球 并发 后台线程并行工作 自定义 并发或串行 每一个都可能有效 异步或同步 第一个问题 主队列是否在工作仅 UI 线程并且不在另一个线程上工作 如
  • 为什么反应路由器在 vercel 上不起作用?

    我正在尝试将无服务器网络发布到 vercel 我想使用react router 这在我的计算机上运行良好 但是当我部署它时它不起作用 有人可以帮助我吗 我想在没有服务器的情况下完成 My main code import React fro
  • ServiceLocator 是一种反模式吗? [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 最近我读过马克 西曼的文章 http blog ploeh dk 2010 02 03 ServiceLocatorisanAnti Patte