当前位置: 首页 > 知识库问答 >
问题:

ServiceLocator是反模式吗?

呼延臻
2023-03-14

最近我读了Mark Seemann关于Service Locator反模式的文章。

作者指出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();
        }
    }
    

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

    很难判断你是否正在引入一个突破性的改变。您需要理解使用服务定位器的整个应用程序,编译器不会帮助您。

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

    此外,作者还提到了单元测试的困难。但是,我们对DI方法不是有问题吗?我们不需要更新所有实例化该类的测试吗?我们将更新它们以传递一个新的模拟依赖项,从而使我们的测试可编译。我看不出更新和时间花费有什么好处。

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

    更新(摘要):

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

    以下是说服我在新项目中不使用Service Locator的主要差异:

    • 最明显和最重要的:服务定位器隐藏类依赖关系
    • 如果您正在使用某些IoC容器,它可能会在启动时扫描所有构造函数以验证所有依赖项,并立即提供有关缺少映射(或错误配置)的反馈;如果您将IoC容器用作服务定位器,则无法做到这一点

    详情请阅读以下精彩回答。

  • 共有3个答案

    彭礼骞
    2023-03-14

    从测试的角度来看,服务定位器是不好的。参见Misko Hevery的Google Tech Talk nice解释,代码示例http://youtu.be/RlfLCWKxHJ0从8:45开始。我喜欢他的比喻:如果你需要25美元,直接要钱,而不是掏出钱包。他还将服务定位器比作一个干草堆,里面有你需要的针,并且知道如何找回它。因此,使用服务定位器的类很难重用。

    丁茂
    2023-03-14

    我还想指出,如果您重构遗留代码,Service Locator模式不仅是一种反模式,而且是一种实际需要。没有人会在数以百万计的代码行上挥舞魔杖,突然间所有的代码都准备好了DI。因此,如果您想开始将DI引入现有的代码库,通常情况下,您会慢慢改变事情,使其成为DI服务,而引用这些服务的代码通常不是DI服务。因此,这些服务将需要使用ServiceLocator来获取那些已转换为使用DI的服务的实例。

    因此,当重构大型遗留应用程序以开始使用DI概念时,我想说服务定位器不仅不是反模式,而且它是将DI概念逐步应用于代码库的唯一方法。

    车靖琪
    2023-03-14

    如果您仅仅因为某些情况下模式不适合而将模式定义为反模式,那么是的,这是一个反模式。但根据这种推理,所有模式都将是反模式的。

    相反,我们必须查看模式是否有有效的用法,对于服务定位器,有几个用例。但是让我们从您给出的示例开始。

    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();
        }
    }
    

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

    在典型的业务线应用程序中,出于这个原因,您应该避免使用服务位置。当没有其他选项时,应该使用这种模式。

    没有。

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

    但是一个更好的例子是ASP.NETMVC和WebApi。你认为是什么使得控制器中的依赖注入成为可能?没错——服务位置。

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

    还有两个更严重的问题:

      < li >对于服务定位,您还添加了另一个依赖项:服务定位器。 < li >如何判断依赖项应该具有哪个生存期,以及应该如何/何时清理它们?

    使用容器的构造函数注入可以免费获得。

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

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

    一些更好的容器也会在启动时验证所有依赖项(通过扫描所有构造函数)。因此,对于这些容器,您可以直接获得运行时错误,而不是在以后的某个时间点。

    此外,作者还提到了单元测试的困难。但是,我们对DI方法不是有问题吗?

    不,因为您不依赖静态服务定位器。你尝试过让并行测试使用静态依赖吗?一点都不好玩。

     类似资料:
    • 问题内容: 我发现很难理解“延迟反模式”。我想我基本上理解了它,但是我还没有看到一个简单的例子,说明什么是服务,具有不同的承诺和一个具有反模式的服务,所以我想尽我所能,但是看到自己不是这样超级了解这一点,我会先澄清一下。 我在工厂(SomeFactory)中具有以下内容: 我检查其对象的原因只是为了在其上添加一个简单的验证层 在下面,在我的指令中: 现在来看,这是一种反模式。因为原始的延迟承诺会捕

    • 我发现很难理解“延迟反模式”。我想我基本上理解它,但我还没有见过一个超级简单的例子来说明什么是服务,有不同的promise和反模式,所以我想我会试着做我自己的,但鉴于我对它的了解不够,我会首先得到一些澄清。 在我看来,这是一种反模式。因为最初的延期promise抓住了错误,并简单地将其吞掉。它不会返回错误,所以当调用这个“getData”方法时,我必须执行另一个catch来获取错误。 如果这不是反

    • 问题内容: 以下是React中的反模式吗?我喜欢这种模式,因为当实例化一个组件时,它在静态函数中为我提供了上下文。然后,我可以导入该类并调用静态方法来修改状态。还是可以通过更好的方式来完成? 问题答案: 显然,这取决于条件,可能是一种反模式,也可能是一个错误。静态类方法不应与类实例一起使用。绑定到特定的组件实例和用途,这只能证明类是单例是合理的(尽管单例也经常是反模式)。如果期望有多个类实例,那么

    • 在软件工程中,一个反面模式(anti-pattern或antipattern)指的是在实践中明显出现但又低效或是有待优化的设计模式,是用来解决问题的带有共同性的不良方法。它们已经经过研究并分类,以防止日后重蹈覆辙,并能在研发尚未投产的系统时辨认出来。 软件设计 抽象倒置(Abstraction inversion):不把用户需要的功能直接提供出来,导致他们要用更上层的函数来重复实现 用意不明(Am

    • 问题内容: 我是一名Java开发人员,他 开始 掌握依赖注入的全部功能,突然间我意识到,没有办法注入静态方法。因此,我想到了: 静态方法是DI反模式吗? 更重要的是:如果我要接受依赖注入,这是否意味着我需要停止对静态方法进行编码?我问,因为没有办法在单元测试期间模拟它们并注入模拟静态变量,这对我来说是个很大的选择。 编辑 :我知道一种“包装”和注入现有的静态方法的通用方法是这样的: …但是我没有问

    • 问题内容: 我对故障排除看起来像是非常痛苦的故障排除经验,如下所示: 该问题很难解决,因为doSomeStuff()引发了异常,这又导致doSomeOtherStuff()也引发了异常。第二个异常(由finally块引发)抛出了我的代码,但是它没有第一个异常(由doSomeStuff()引发)的句柄,这是问题的真正根源。 如果代码改为这样说,那么问题就很明显了: 所以,我的问题是这样的: 在没有任