Ioc 器管理的应用程序设计,前奏:容器属于哪里?
我将讨论一些我认为应该应用于“容器管理”应用程序设计的原则。
模式1:服务字典
字典或关联数组是我们在软件工程中学到的第一个构造。 很容易看到使用依赖注入组成对象的字典和IoC容器之间的类比:
未使用容器
使用容器
这两看起来不同的是:
一些花哨的新术语'Register'和'Resolve'被使用,而不是索引器访问
记录器是通过反射创建的,在相互依赖的情况下保存一些打字和配置代码
如果你进入一个已经建立了依赖注入容器的项目,并且必须弄清楚如何编写你需要与系统的其他部分进行交互的代码。
模型2:抽象 new()运算符
经常称赞依赖注入容器是为了消除实现类型依赖。回到我们的例子,我们已经提出了一个独立于提供它的实现类型的ILogger请求(在这种情况下为ConsoleLogger)。
ASP.NET MVC等Web框架使用我们的依赖注入容器以类似的方式创建Controller实例。
将容器想象为一个抽象的new()运算符会更有意义?
将上面代码重构成下面代码
下面代码
在这里,我们的容器使用不仅抽象出了我们的MailTransport的具体类型,还负责配置其主机属性。 (配置必须是我们的abstract new()运算符的一个特性,因为实例上的配置参数取决于它的具体类型而不是它提供的服务。)
设计成果:the Global Container
遵循这些思想模式之一鼓励您将容器视为您从中检索的东西。即使是“容器”这个名字也会推动我们朝这个方向发展。
从这个角度来看,应用程序架构设计任务中没有太多的挑战!
我们将在一个类上创建一个静态属性Container,例如Global,并使用Global.Container.Resolve <MyService>()从它获取实例。
这段代码应该熟悉另一个名称的模式:静态服务定位器(the static Service Locator)。
the Global Container 作为服务定位器的问题
这种模式可以被正确地称为反模式有充分的理由:
脆弱性。容器就像一桶全局变量。强制组件公开声明依赖关系所实现的所有健壮性都会丢失。意外的依赖关系可能出现在应用程序的看似无关的部分之间,使维护变得复杂。
降低组合性。当注入依赖关系时,可以将容器配置为向不同的消费者提供不同的实现。组合独立构建的组件时,这通常是必需的。当从全局容器检索依赖项时,容器不知道请求者的身份,并且每次都*返回相同的实现。
有限的重用。全局容器所在的位置可能会限制依赖于它的组件的可重用性。将组件迁移到WPF应用程序将需要更改代码,如果它们依赖于附加到HttpApplication类的全局容器。
实施问题。在全球基于容器的解决方案中,并发性,重入性,循环引用检测和组件生命周期管理更加困难/混乱。
恢复控制:一般来说,这些问题的产生是因为直接调用全球容器是恢复控制。在容器管理的应用程序中,容器负责将正确的实例放到正确的位置以完成工作。调用一个全球容器以一种不易管理的方式来承担一部分责任。有时候,由于许多当代框架中的设计决策,the Global Container是必要的,但为了推进软件工程的发展,我们需要超越这一点。
箱子里的思考
上面列出的心理模型应该已经敲响了一些警钟:
该容器是一个服务字典? 不挂断! 如果我两次要求同样的服务,为什么我有时会得到不同的实例?
好问题。 配置一个依赖注入容器很容易,每次解析时都会返回一个新的ConsoleLogger实例。 不是很像字典,是吗?
所以容器真的是一个抽象的new()操作符? 等待! 如果我有时得到相同的共享实例,我何时可以Dispose()它?
这两种模式甚至不兼容。 有时候容器提供了新的实例,但有时从调用中返回的是一个单例,或者是一个将在某种上下文*享的实例,比如当前事务。
所以事情现在令人困惑。
修订后的思想模型:一个互相关联的对象系统
问题来了:看看Autofac主页上的例子。 它显示了Autofac中如何使用Register()和Resolve()。 这是依赖注入的一个相当常见的例子。 注意缺少了什么?
此示例中最重要的类不是ContainerBuilder或Container。 从应用程序架构的角度来看,我们需要看到Straight6TwinTurbo和Car类!
解析操作只是找到一个独立系统的入口点的一种方法。
整个应用程序驻留在容器中。 除了无论什么入场点滚球,都没有“外线”。 从这个角度理解IoC并不是围绕像Register()和Resolve()这样的外部API。
使用这个模型,容器能够在合适的时间将组件实例放在正确的位置,应用程序设计重新关注组件本身的实现。
设计结果:注入上下文
在实施我们改进的IControllerProvider时,这个世界的新视角给我们带来了挑战。 该服务需要返回(可能)新的,参数化的Controller类型实例。
通常在当今应用中实现的解决方案是允许容器为控制器提供商提供上下文:
我们在这里假设ControllerProvider本身在容器中托管。 IContext是Autofac自动提供给需要它的任何组件的接口。 您可以在任何其他流行的IoC容器中创建和使用等效界面(以下链接了一个MEF示例)。
IContext以可控方式向应用程序提供容器的实例解析功能。 实现IContext的对象提供了容器的服务,但可能不是容器本身。
由于此模式为容器或应用程序开发人员提供了定制或代理发送给任何特定组件的IContext实现的机会,因此与全局容器关联的大多数问题都会得到缓解。
容器管理的应用程序设计
如果你觉得这篇文章及其解释有些不完整,那你是对的。 正如这篇文章的标题所表明的那样,它是关于该主题的一系列介绍。 我没有全部的答案,我甚至没有全部的问题.。
【本文为本人翻译,原文查看】
近期在搞区块链方面的技术,同时本公众号也会分享关于.net core的技术文章,欢迎大家关注!