我正在阅读“.NET Platform 2nd Edition 上的依赖注入”(俄语,春季发布)。作者清楚地写了几次-
使用 DI 容器时,组合根必须是使用该容器的唯一位置。在组合根之外使用 DI 容器会导致 Service Locator 反模式,这将在下一章中讨论。
举个例子,说明它很丑。好吧,假设我同意。
我有一个 WPF(或任何其他)桌面应用程序。我认为每次按下 UI 中的某个“按钮”都是一个单独范围内的操作。那些。API 的一部分应该创建与生命相对应的实例。通常,在 ICommand.Execute 处理程序中,有以下伪代码:
void ICommand.Execute(object parameter)
{
using (DI.CreateSomeScope())
new SomeCommand(...).Execute(parameter);
}
有两个问题,第一个很重要,第二个在评论中整理出来,但也许有人对这个问题有自己的看法 - 也分享一下:
- 我怎样才能在这里声明一个范围?根据本书和大多数 DI 框架,范围完全是容器的责任。将它拖到这里是一个显式的服务定位器。
- 怎么办
new SomeCommand?嗯,即 例如,程序的主窗口已经在运行,我想要的一切都显示出来了。然后用户点击“设置”。我需要创建一个设置 VM,然后将其分配给一个 binned 属性以更改应用程序。并且 DI 再次负责创建不稳定的(vm 非常适合不稳定的)依赖项。我真的不明白如何在应用程序生命周期的中间实例化对象。在书中,作者主要展示了基本的例子,或者是aspnet,其中有一个预谋的点——创建一个控制器来处理请求。
让我们首先定义什么是作用域,什么是对象的生命周期。
范围是存在许多对象的有限范围。例如,它可能绑定到请求上下文,可能绑定到 WPF 中的视图,可能是人为生成并绑定到某些业务场景。例如,我曾经在 WPF 中编写了一个应用程序,其中的每个视图都是它自己的范围。
对象的生命周期是容器管理对象的方式,例如可以在每次需要时重新创建对象,或者在每个容器有 1 个对象时成为一个 sington。
如果事先知道范围(例如,请求范围),那么您可以指定该对象将是,例如,某个特定范围的单例,但同时您指定范围和生命周期。
范围通常表示为容器的层次结构(至少对我来说是这样)。也就是有一个主容器,还有一个子容器,只供View使用。在这种情况下,如果解析时在子容器中没有找到该类型,则会在父容器中进行搜索。这样的设备允许我将子 View / ViewModel 注册为子容器中的单例,并且它们的所有其他依赖项都通过这个子容器管理。因此,我有一个 View / ViewModel 级别的 Scope,但可以从主容器接收对象如果类型未在子项中注册,则为容器。例如,每个视图有一个 eventBus - ViewEvenBus - 作为子容器中的单例,每个应用程序有一个 eventBus - ApplicationEvenBus - 作为根容器中的单例。
现在让我们继续讨论服务定位器模式。让我们想想为什么它被认为是反模式?我的观点是,如果你有能力在不打算解决的类中解决任何你想要的问题,那么你就会失去控制。在大型类中,您不仅很难跟踪生命周期,而且通常很难理解类的确切依赖关系,因为获取依赖关系遍布整个代码,而不是集中在一个地方。
因为,例如,我有时允许在类中创建对象,但为此我使用工厂。例如,如果一个类显式依赖
IFactory<T>,那么您已经明白 1) 该类不能创建除 T 以外的任何东西,2) 它具有显式写入构造函数中的这种依赖关系。所以如果你试图在视图中声明一个范围,你可能做错了什么。范围是应该在父类级别上工作的东西,无论是工厂类还是容器类。
因为当我使用 UI 时,
因此,