最近我读了 Mark Seemann 的文章关于服务定位器反模式的文章。
作者指出了 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();
}
}
这就是我的误解开始的地方,作者说:
很难判断你是否正在引入一个突破性的改变。您需要了解使用 ServiceLocator 的整个应用程序,而编译器不会帮助您。
但是等一下,如果我们使用 DI 方法,我们会在构造函数中引入一个带有另一个参数的依赖项(在构造函数注入的情况下)。问题依然存在。如果我们可能忘记设置 ServiceLocator,那么我们可能会忘记在 IoC 容器中添加一个新的映射,而 DI 方法也会遇到同样的运行时问题。
此外,作者还提到了单元测试的困难。但是,我们不会对 DI 的方法有异议吗?我们不需要更新所有实例化该类的测试吗?我们将更新它们以传递一个新的模拟依赖项,以使我们的测试可编译。我看不出这种更新和花费时间有什么好处。
我不是在为服务定位器辩护。但这个误会让我觉得我失去了很重要的东西。有人能消除我的疑虑吗?
更新(摘要) :
我的问题“服务定位器是反模式吗”的答案实际上取决于具体情况。我也绝对不会建议把它从你的工具清单上划掉。当您开始处理遗留代码时,它可能会变得非常方便。如果您足够幸运,能够处于项目的最初阶段,那么 DI 方法可能是一个更好的选择,因为它比服务定位器有一些优势。
这里有一些主要的不同之处,它们让我决定不在我的新项目中使用服务定位器:
详情请阅读下面给出的精彩答案。