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 是一种反模式吗? [关闭] 的相关文章

  • 这叫什么类型的建筑?

    对于我当前正在开发的 Web 应用程序 ASP NET MVC 我们采用以下架构 Data Access Layer 将数据持久保存到任意数据库的逻辑 Domain 数据模型 Service Layer 业务逻辑 例如订单处理 账户管理等
  • 使用“旧”对象引用使用构建器模式创建新对象

    我正在尝试构建器模式 并陷入如何向新创建的对象添加新 属性 的困境 public class MsProjectTaskData private boolean isAlreadyTransfered private String req
  • C++ 包装类的设计

    我必须使用一个界面非常笨拙的旧类 由于我无法更改它并且依赖它 因此我想构建一个包装器 提供一个干净的界面 假设我有课笨手笨脚的班级 基本上 我有三种方法 1 参考会员 Class Wrapper public Wrapper ClumsyC
  • 为什么我收到错误“错误 C2259:...无法实例化抽象类”?

    任何帮助都是appriced 我正在研究 C 工厂模式 但收到此错误 1 gt c users brian documents visual studio 2010 projects cst276lab 3 guitar hpp 456 错
  • STL(标准模板库)中使用的设计模式

    我正在学习STL和设计模式 我想知道是否有任何文档或链接可以解释如何在 STL 中实现设计模式 我做了谷歌但无法获得太多数据 我希望你的意思是 哪些设计模式可以在STL中识别 STL 堆栈是一个容器适配器 适配器是一种设计模式 迭代器也是一
  • 在 AngularJS 中覆盖运行时的依赖关系

    我有一个服务叫 doggedHttp 它公开了与 http 现在我想创建一个 doggedResource服务是有角度的 resource服务之上 doggedHttp代替 http 换句话说我想注入 doggedHttp as the h
  • 在简单注入器中注册具有多个构造函数和字符串依赖项的类型

    我正在尝试弄清楚如何使用 Simple Injector 我在项目中使用了它 注册简单服务及其组件没有任何问题 但是 当组件具有两个以上实现接口的构造函数时 我想使用依赖注入器 public DAL IDAL private Logger
  • 如何使用 Spring Resource.groovy 正确注入 Grails 服务

    使用 Grails 2 2 1 我定义了以下 Grails 服务 package poc class TestService def helperService class HelperService 我已经用过TestService如下
  • 三层 Asp.Net 应用程序中的异常处理

    1 据我了解 在three tierAsp Net应用程序我们应该通过以下方式实现异常处理 a 我们应该把try catch围绕代码块 位于三层中的任何一层 我们希望页面能够从该代码块正常恢复 当此代码生成异常时 b 我们不应该放try c
  • 如何为 asp.net MVC 5 配置 StructureMap

    我遇到以下错误 我的设置与 asp net mvc 4 类似 没有为此对象定义无参数构造函数 描述 安 当前网页执行期间发生未处理的异常 要求 请查看堆栈跟踪以获取有关的更多信息 错误及其在代码中的起源 异常详细信息 System Miss
  • 依赖注入系统中的事件朝哪个方向发展?

    上或下 我是一个非常注重视觉的人 我将我的应用程序视为一个层次结构 顶部是根 底部是叶子 我还了解到 在 DI 系统中 容器不知道其所包含对象的职责 功能 相反 所包含的对象知道它们的上下文 因为上下文 依赖项 被注入 UP 非 DI 方式
  • MVC3、Ninject、MvcSiteMapProvider - 如何将依赖项注入到重写方法

    我有一个正在使用的 MVC3 应用程序Ninject http ninject org and Mvc站点地图提供者 http mvcsitemap codeplex com 我创建了此类 MvcSiteMapProvider 使用它来动态
  • 测试 - 存根服务方法未定义

    我已经在非常简单的代码上编写了一个非常简单的测试 但由于某种原因存根服务方法未定义 当我使用 Jasmine Spy 时 它可以工作 但对于这样一个简单的任务 有人可以解释一下为什么会发生这种情况吗 我删除了 import 语句只是为了减少
  • 如何简化 ASP.NET MVC 中的全状态交错模式对话框

    我需要在多对多模式对话框中保留状态渐进增强 http en wikipedia org wiki Progressive enhancementASP NET MVC 项目中的方式 在我的代码中 当禁用 javascript 时 模式对话框
  • DI:IDisposable 对象的处理寿命

    所以我正在开发我的 DI IoC 容器OpenNETCF IoC http ioc codeplex com我有一个 合理的 功能请求 为容器集合中的 IDisposable 项目添加某种形式的生命周期管理 我目前的想法是 由于我无法查询对
  • 在实现接口的类上强制使用单例模式

    我最好用一个例子来解释这个问题 我有一个接口模型可用于访问数据 模型可以有不同的实现 可以以各种格式表示数据 例如 XMl txt 格式等 Model不关心格式 可以说这样的一个实现是myxml模型 现在我想强迫myxml模型以及其他所有实
  • 发布者-订阅者与观察者[关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 这是依赖注入吗?这是一种不好的做法吗?

    我有一个小框架 我是这样编码的 我不确定这是否称为依赖注入 我不知道它是否像设计模式 我也不知道并且想知道是否通过 this因为 param 是一种不好的做法 看看这个 不是一个有效的示例 只是将这些代码写入浏览器中以供解释 This is
  • 工厂模式中创建者的角色

    我无法理解为工厂类定义抽象类 接口的作用 这是我在网络上的所有教程中总是看到的东西 有人可以阐明 CreatorInterface 的重要性吗 工厂模式参考UML图 https i stack imgur com 3VpUM png 为了以
  • java异常处理策略[关闭]

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

随机推荐

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

    我在页面内有一个控件 该控件具有以下模式弹出窗口扩展程序
  • 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