[英]DI composition root: how does it ensure compile-time resolution checking
我已经阅读了Mark Seeman关于依赖注入的几篇文章,特别是避免使用Service Locator模式的原因:
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);
但这意味着合成根不仅必须在启动时解析所有依赖关系,而且必须实例化整个对象图,否则我们仍然不知道所有必要的依赖关系都已注册:
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缓冲区从输入端口构造到特定于该设备类型的许多解析器的“管道”,并以多个使用方结束各种解析的消息。
提前组成这些对象图似乎在应用程序启动时是不可能的。 即使可以延迟,我仍然看不到如何提前获得对象图形构造失败的指示 。
简而言之,Service Locator的问题在于它隐藏了类的依赖关系,从而导致运行时错误而不是编译时错误,并且使代码更难维护,因为不清楚何时引入中断。更改。
但这意味着合成根不仅必须在启动时解析所有依赖关系,还必须实例化整个对象图
如果您应用Pure DI (即应用Dependency Injection模式,但没有DI容器),则将立即获得编译时支持。 使用DI容器,您将必须在运行时进行这些检查,但这并不意味着您必须在启动期间执行此检查,尽管我会说这是首选。 因此,如果在启动时检查容器的配置不会导致性能问题,则应执行此操作。 否则,您可以将此验证步骤移至单元测试。
在实际应用中这有多可行?
这是完全可行的。 我构建大型应用程序时,通常会在容器中注册数百到上千个服务,并且我总是会验证(并诊断 )容器的配置,从而避免了许多常见的配置错误,这些错误很容易造成且很难追踪。
这真的意味着您不应该在构造函数之外的任何地方实例化任何东西吗?
组成根负责创建服务。 这本身并不意味着应该在启动期间创建所有服务,因为您可以将对象图各部分的创建延迟到运行时。 但是,我的首选工作方式是使所有注册的服务成为单例(申请期间的一个实例)。 这使得在应用程序启动期间创建所有服务真的非常容易(且便宜),并迫使您进入一个更严格的模型,在该模型中SOLID违规和其他DI不良行为会更快地弹出。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.