【问题标题】:Service locator - worth it?服务定位器 - 值得吗?
【发布时间】:2012-07-02 06:32:22
【问题描述】:

我们有一个大型解决方案(> 100 个项目),几乎每种类型都使用服务定位器(示例 1)或我们自己的类型字典(示例 2)进行实例化。

例如我们有:

IQuote quote = Registry.Resolve<IQuote>(); 

IQuote quote = Registry.Find<IQuote>(args);

第二个示例转到配置文件以查找要使用反射实例化的具体对象。

在跟踪代码时会变得更加困难 - 因为不清楚使用的是什么具体类型 - 所以我们必须在尝试学习部分代码时多次检查映射。以上述为例,按下 F12 键:quote.DoSomething() 将带您进入接口定义。

实现起来也有点困难——我们需要一个接口 + 具体类 + 配置映射,而替代方案只有 1 个类。

想一想 - 我不知道有任何东西被“换掉”为另一种类型 - 所以虽然我们已经实现了 IoC,但我们没有使用它,或者至少 - 很少使用它。

那么 - 它真的值得吗?我们是否错误地/太多地实施了它?我是不是误会了什么?

【问题讨论】:

  • 关于 F12 仅将您发送到接口定义因此不是很有帮助,请记住,这部分是 Visual Studio 的限制,而不仅仅是 DI 的错误:VS 可以 进行改进,使其指向被调用的实际方法。显然 VS 只能在调试会话期间执行此操作,并且只有在明确知道对象的运行时类型时才能这样做。
  • 这是一个很好的问题,我真的很想阅读一些关于此的意见,但它可能更适合programmers.stackexchange.com
  • @stakx:如果 .net 中有 DI 的事实上的标准实现(我不知道),VS 自动识别 DI 类解析可以工作。
  • @Vlad,你误解了我的意思。我并不是说 VS 可以解析您的 DI 容器配置,但如果您在定义和使用变量的范围内进行调试,调试器可以检查您的 quote 变量的实际类型,然后跳转到该类型的定义而不是 @ 987654326@ 的静态类型的定义。改进有限且不是很大,但在某些情况下可能会有所帮助。
  • @stakx:哦,我明白了。就像现在做的那样,F12 使用静态分析,因此它可以在程序不运行时使用。也许其他一些命令应该(是否?)在运行时可用,检查实际的运行时类型?这将是有价值的。

标签: .net design-patterns service-locator


【解决方案1】:

你们正在使用的是一个被认为是反模式的服务定位器, 因为:

  1. 所有单元测试都必须使用Service Locator(DI 不涉及DI Container
  2. 您将整个架构耦合到服务定位器(在 DI 中,只有 Composition Root 使用 DI 容器
  3. 您无法立即看到组件的依赖关系(使用 DIConstructor Injection
  4. 您的单元测试变得更加复杂,因为您必须注意自己的配置,以免其他测试错误地使用其他配置(拆除)。

http://blog.ploeh.dk/2010/02/03/ServiceLocatorIsAnAntiPattern.aspx

【讨论】:

    【解决方案2】:

    在我看来,您不需要在处理每个班级时都考虑到 DI。我会使用以下策略:

    1. 确定模块边界。
    2. 在同一模块中,尽可能使用具体类。
    3. 对于模块间通信,尽可能使用 DI。

    模块应该是相对细粒度的。

    在一些常见的地方您需要使用 DI,通常是可替换的数据源和(不经常)算法。使用常识来检查某些东西是否需要更换。如果您发现需要 DI 或遇到问题,请不要犹豫,尽早进行重构。

    【讨论】:

      【解决方案3】:

      你展示的不是依赖注入,它是一个可配置的注册表(这也是一种模式,但完全不同)。所以,是的,看起来是个坏主意,也是一种误解。

      为了能够从某些设计模式中获益,您必须预先了解其好处。如果你不认识他们,那么你可能不应该打扰。

      【讨论】:

      • +1。我在发表同样的评论后看到了你的回答。
      • 实际上,情况更糟,因为它似乎是服务定位器反模式。正确使用服务定位器并不是一件坏事。在实现中某处使用的服务定位器被认为是一种反模式(这里似乎就是这种情况)
      【解决方案4】:

      如果您进行单元测试,某种依赖注入对于存根或模拟...依赖项至关重要。 (单元测试不应该遇到依赖,比如文件系统、数据库、网络等)

      【讨论】:

        【解决方案5】:

        我认为您将依赖注入的更一般概念与使用 DI 容器混淆了,后者是实现非常灵活的 DI 形式的一种手段。 Here 是一篇很好的文章,将 DI 解释为一个概念。

        在您的情况下,在我看来,您可以摆脱 DI 容器,但如果只是为了可测试性(正如其他人已经指出的那样),可能仍希望保留某种形式的 DI。一个常见的实现模式是实现“广泛的”构造函数,通过它可以注入依赖项并在单元测试中使用。此外,还会有“更窄”的构造函数,它们将实例化具体类并将它们传递给“广义”构造函数。这些可以在生产代码中使用,直到需要实际交换依赖项。

        示例:

        class MyClass
        {   
            // This will be called from production code
            public MyClass() : this(new Foo(), new Bar())
            {}        
        
            // This will be called from tests and can be used in production code if needed
            public MyClass(IFoo foo, IBar bar)
            {
               //do whatever you want to do in the ctor here
            }
        }
        

        【讨论】:

        • 请注意:这称为构造函数注入,在大多数情况下是最好的方法(除了属性注入,如果需要)
        猜你喜欢
        • 2012-06-24
        • 1970-01-01
        • 2012-09-09
        • 1970-01-01
        • 1970-01-01
        • 2018-11-22
        • 1970-01-01
        • 2018-10-27
        • 2010-11-28
        相关资源
        最近更新 更多