【发布时间】:2015-10-27 09:58:17
【问题描述】:
我已经阅读了 Mark Seeman 关于依赖注入的几篇文章,特别是避免使用服务定位器模式的原因:
- Service Locator is an Anti-Pattern
- Service Locator violates encapsulation
- Service Locator violates SOLID
Service Locator 存在问题的基本思想是它可能在运行时失败:
public class OrderProcessor : IOrderProcessor
{
public void Process(Order order)
{
var validator = Locator.Resolve<IOrderValidator>();
if (validator.Validate(order))
{
var shipper = Locator.Resolve<IOrderShipper>();
shipper.Ship(order);
}
}
}
var orderProcessor = new OrderProcessor();
// following line fails at compile time if you
// forget to register all necessary services - and
// you don't have a way of knowing which services it needs
orderProcessor.Process(someOrder);
但这意味着,Composition Root 不仅必须在启动时解决 所有 依赖关系,还要实际实例化整个对象图,否则我们仍然不知道所有必要的依赖关系都已注册:
private static void Main(string[] args)
{
var container = new WindsorContainer();
container.Kernel.Resolver.AddSubResolver(
new CollectionResolver(container.Kernel));
// Register
container.Register(
Component.For<IParser>()
.ImplementedBy<WineInformationParser>(),
Component.For<IParser>()
.ImplementedBy<HelpParser>(),
Component.For<IParseService>()
.ImplementedBy<CoalescingParserSelector>(),
Component.For<IWineRepository>()
.ImplementedBy<SqlWineRepository>(),
Component.For<IMessageWriter>()
.ImplementedBy<ConsoleMessageWriter>());
// Everything must be resolved AND instantiated here
var ps = container.Resolve<IParseService>();
ps.Parse(args).CreateCommand().Execute();
// Release
container.Release(ps);
container.Dispose();
}
这在实际应用中的可行性如何?这真的意味着你不应该在构造函数之外的任何地方实例化任何东西吗?
(附加信息)
假设您有一项服务应该处理来自多种测量设备(不同的连接类型、协议或同一协议的不同版本)的传入连接。每当您获得新连接时,服务都应该构建一个“管道”,从输入端口,通过 fifo 缓冲区,到特定于该设备类型的许多解析器,以多个消费者结束各种解析的消息。
预先组合这些对象图在应用程序启动时似乎是不可能的。即使它可以延迟,我仍然看不到如何获得早期(-er)指示对象图构造将失败。
这似乎是main problem with service locators,我不知道如何避免它:
简而言之,Service Locator 的问题在于它隐藏了类的依赖关系,从而导致运行时错误而不是编译时错误,并且使代码更难维护,因为不清楚何时会出现引入重大更改。
【问题讨论】:
-
这在现实世界的应用程序中有多可行? 相当可行blog.ploeh.dk/2011/03/04/Composeobjectgraphswithconfidence 我一直都在这样做;这不是一个抽象的练习,而是对我(和其他人)几十年来如何构建应用程序的描述。
-
Here is another reason 为什么服务定位器是反模式。它限制了对象的可组合性。
-
我们也可以对服务定位器进行编译时或运行时分析。就确保配置服务而言,DI 容器和 SL 注册表之间没有太大区别。
-
@MarkSeemann:我猜你一直在使用它:-)。我知道我必须改变我的心态才能了解如何使 DI 适用于我的应用程序类型,但这是我的观点:我不确定如何。例如,在接受来自数百个不同类型/协议的设备的连接的服务器应用程序中,我必须有一种方法来管理单个设备“管道”的生命周期,从传入端口,通过各种解析器(根据设备类型实例化/protocol),发送给最终消费者。这是应用程序对象图的一个相当“动态”的部分,我看不出如何“根”它?
-
如果不能在启动时组成一个完整的Services对象图,那一定是因为需要一些运行时的值;至少,我想不出任何其他原因。运行时值是输入,您必须假设所有输入都是邪恶的。在邪恶的输入,事情会失败。如果网络出现故障,事情可能会失败。等等……
标签: c# design-patterns dependency-injection inversion-of-control service-locator