c端产品后台功能梳理(七爪源码ServiceLocator是反模式吗)
c端产品后台功能梳理(七爪源码ServiceLocator是反模式吗)第一种方法只是强制您直接设置所有依赖项。即使使用像 ASP 这样的框架,构造函数中的所有依赖项也允许您的 DI 容器验证图树。1.运行时错误而不是编译时class OrderService { . . . public OrderService(ITimeService timeService ILogger logger) { _timeService = timeService; _logger = logger; } . . . }和一个带有 ServiceLocator 的。 依赖项被隐藏并在内部解决:class OrderService { . . . public Ord
是的。 很抱歉没有再保守一点秘密,但也没什么好说的。 这是一件可怕的事情,你应该尽可能避免它。
但是,你有没有想过为什么它是反模式? 如果在某些情况下它仍然是合适的选择?
最近一直在和朋友争论这个问题。 而且我对诸如“它让你依赖 ServiceLocator”、“它就像全局变量”或“它违反了这个或那个原则”之类的论点并不感兴趣。 所以我想试一试,看看它为什么这么糟糕。
对于那些不知道 ServiceLocator 是什么的人,请看一下健康类。 它的所有依赖项都通过构造函数传递:
class OrderService
{
. . .
public OrderService(ITimeService timeService ILogger logger)
{
_timeService = timeService;
_logger = logger;
}
. . .
}
和一个带有 ServiceLocator 的。 依赖项被隐藏并在内部解决:
class OrderService
{
. . .
public OrderService()
{
_timeService = Locator.GetService<ITimeService>();
_logger = Locator.GetService<ILogger>();
}
. . .
}
尽管看起来像 DI 流程简化了,但它会导致很多问题。
现在,当您知道 ServiceLocator 是什么之后,让我们来看看它为什么这么糟糕:
1.运行时错误而不是编译时
第一种方法只是强制您直接设置所有依赖项。即使使用像 ASP 这样的框架,构造函数中的所有依赖项也允许您的 DI 容器验证图树。
使用定位器,您不会知道服务无法正确实例化,因此无法正常工作。仅当您收到来自系统用户的恼怒电话时。在真实的场景中,在整个项目中维护数百个类将是一场真正的噩梦。
2. 生命周期
你还记得你上次在服务中编写 Dispose() 方法是什么时候吗?我也没有。那是因为 DI 容器通常自己创建和处置服务。
使用 ServiceLocator,您将被迫自己维护服务的生命周期。你还不害怕吗?想象一下处理另一个服务使用的依赖项。想象一下处理另一个线程使用的依赖项。想象一下以不同的生命周期处理依赖项。现在呢?如果我是你,我会投降,让我的服务随心所欲地生活
3.单元测试
在第一个示例中,我可以看到自己在模拟依赖项。现在尝试在第二个中这样做。
你可能会想,“如果有一个默认构造函数,它应该可以工作吗?”我不会那么肯定。
var orderService = new OrderService();
IntelliSense 没有任何帮助,因此您需要打开该类并发现其依赖项。 很高兴这是您的代码。 但是如果某些第三方库决定使用 ServiceLocator 的话。 纯粹的恐怖。 试着弄清楚你应该模拟什么。
“事情没那么复杂。 只需导航到一个类并查看”,您可能会想。
所以我们在这里。 现在一切都在嘲笑。
Locator.Register(Mock.Of<ITimeService>());
Locator.Register(Mock.Of<ILogger>());
var orderService = new OrderService();
由于 ServiceLocator 是静态的,我们需要为每个单元测试重新设置 decencies。 惊喜,并行执行也将是一个问题。
我们可以尝试通过注入 ServiceLocator 而不是静态实例来修复它:
class OrderService
{
. . .
public OrderService(ILocator locator)
{
_timeService = locator.GetService<ITimeService>();
_logger = locator.GetService<ILogger>();
}
. . .
}
对于单元测试来说更好一点。没有相互独立的显式重置和单元测试。但它真的有帮助吗?
尽管如此,IntelliSense 还是没用的。我们知道服务取决于定位器,但仅此而已。祝你好运,弄清楚你需要什么服务。此外,向您的服务添加另一个依赖项,所有测试都将失败
说缺点。但是,我们真的可以使用它吗?我知道关于它已经说了很多废话,但仍有一些地方我可以证明使用它是合理的。
1 遗留代码
我们都必须处理遗留代码。尽管有很多技巧、最佳实践和模式,但有时花时间重构所有内容并不合适。或者你可能会像我一样变得懒惰所以当重构大型遗留应用程序而不是重新设计它时,只需使用 ServiceLocator。然后稍后修复它。记住,你怎么吃大象?一次咬一口!
2 框架限制
每个人都有他困扰的罪。不管你多么喜欢你的框架,它都是由一个和你一样的人用皮肤和骨头制成的。甚至框架的开发人员有时也会让我们处于尴尬的境地,迫使我们与不友好的АРІ合作。
这些示例之一可能是 ASP 中的 BackgroundService。这是一项启动 Jobs 的特殊服务。但是,该服务的行为也很特殊。它注册为 Singleton,这意味着我们不能像使用 Controller 那样在其中注入 Scoped 服务。解决方案是使用 ServiceLocator 并即时实例化所需的依赖项。
public class MyBackgroundService : BackgroundService
{
private readonly IServiceScopeFactory _serviceScopeFactory;
public MyBackgroundService(IServiceScopeFactory serviceScopeFactory)
{
_serviceScopeFactory = serviceScopeFactory;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
using (var scope = _serviceScopeFactory.CreateScope())
{
var appService = scope.ServiceProvider.GetRequiredService<OrderAppService>();
await appService.Execute(stoppingToken);
}
}
}
这种方法并不理想,但至少您不会为框架代码编写单元测试,因此对您来说不会那么头疼。
概括
使用服务定位器的问题不在于您依赖于特定的服务定位器。
它只是通过创建一个需要大量样板代码来支持的糟糕 API 来给开发人员带来糟糕的体验。
它不仅需要您深入研究代码以了解如何处理依赖关系,而且还需要隐藏一些 DI 问题,例如循环依赖关系。 这些错误仅出现在运行时,这会引发大量错误。
因此,ServiceLocator 的使用显着增加了对代码和测试的维护,并使您专注于 DI 错误而不是做一些重要的事情。
关注七爪网,获取更多APP/小程序/网站源码资源!