最近我读了Mark Seemann写的文章,讲述了Service Locator反模式的两个主要原因:
API使用问题(我对此完全没有问题):
当类使用Service Locator时,很难看到它的依赖关系,因为在大多数情况下,该类只有一个“无参数构造函数”。相比之下,使用DI方法通过构造函数的参数明确地公开依赖项,因此可以在IntelliSense中轻松地查看依赖项。维护问题(这让我感到困惑):
考虑以下示例:
我们有一个使用Service Locator方法的类“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();
}
}
这里是我的误解的起点。作者说:“判断你是否引入了破坏性更改变得更加困难。你需要了解Service Locator所在的整个应用程序,编译器无法帮助你。”
但是等一下,如果我们使用DI方法,我们将在构造函数中引入另一个参数的依赖项(在构造函数注入的情况下)。问题仍然存在。如果我们忘记设置Service Locator,那么我们可能会忘记在IoC容器中添加新映射,而DI方法也会有相同的运行时问题。
此外,作者提到单元测试的困难。但是,我们使用DI方法会遇到问题吗?难道我们不需要更新实例化该类的所有测试吗?我们将更新它们以传递一个新的模拟依赖项,只是为了使我们的测试可编译。我没有看到任何从这种更新和时间开销中获益的好处。
我并不是在试图为Service Locator方法辩护。但是这种误解让我觉得我失去了非常重要的东西。可以有人解除我的疑虑吗?
更新(摘要):
对于我的问题"Service Locator是反模式吗?"的答案真的取决于具体情况。我绝对不建议从你的工具列表中删除它。当你开始处理旧代码时,它可能非常方便。如果你很幸运,正处在项目的开始阶段,那么DI方法可能是更好的选择,因为它比Service Locator具有一些优势。
以下是主要差异,使我决定不在我的新项目中使用Service Locator:
- 最明显和重要的: Service Locator隐藏了类之间的依赖关系。 - 如果您正在使用某个IoC容器,它很可能会在启动时扫描所有构造函数以验证所有依赖项,并提供有关缺少映射(或错误配置)的即时反馈;如果您将自己的IoC容器用作服务定位器,则不可能实现这一点。
欲知详情,请阅读下面给出的优秀答案。