最近,我阅读了Mark Seemann的有关Service Locator反模式的文章。
作者指出了ServiceLocator是反模式的两个主要原因:
API使用问题(我非常满意)
当类使用Service定位器时,很难看到其依赖关系,因为在大多数情况下,类只有一个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更具优势。
以下是使我确信不要对新项目使用Service Locator的主要区别:
- 最明显和最重要的:服务定位器隐藏类依赖关系
- 如果您正在使用某些IoC容器,它可能会在启动时扫描所有构造函数以验证所有依赖关系,并就缺少的映射(或错误的配置)立即提供反馈;如果您将IoC容器用作服务定位器,则这是不可能的
有关详细信息,请阅读下面给出的出色答案。