[ASP.NET Core 3框架揭秘] 依赖注入:IoC模式

时间:2022-08-31 08:54:17

原文:[ASP.NET Core 3框架揭秘] 依赖注入:IoC模式

正如我们在《依赖注入:控制反转》提到过的,很多人将IoC理解为一种“面向对象的设计模式”,实际上IoC不仅与面向对象没有必然的联系,它自身甚至算不上是一种设计模式。一般来讲,设计模式提供了一种解决某种具体问题的方案,但是IoC既没有一个针对性的问题领域,其自身也没有提供一种可操作性的解决方案,所以我们更加倾向于将IoC视为一种设计原则。很多我们熟悉的设计模式背后都采用了IoC原则,接下来我们就来介绍几种典型的“设计模式”。

一、模板方法

提到IoC,很多人首先想到的是依赖注入,但是在我看来与IoC联系得最为紧密的倒是另一种被称为“模板方法(Template Method)”的设计模式。模板方法模式与IoC的意图可以说不谋而合,该模式主张将一个可复用的工作流程或者由多个步骤组成的算法定义成模板方法,组成这个流程或者算法的单一步骤则实现在相应的虚方法之中,模板方法根据预先编排的流程去调用这些虚方法。这些方法均定义在一个类中,我们可以通过派生该类并重写相应的虚方法的方式达到对流程定制的目的。

对于前面我们演示的这个MVC的例子,我们可以将整个请求处理流程实现在一个MvcEngine类中。如下面的代码片段所示,我们将请求的监听与接收、目标Controller的激活与执行以及View的呈现分别定义在5个受保护的虚方法中,模板方法StartAsync根据预定义的请求处理流程先后调用这5个方法。

[ASP.NET Core 3框架揭秘] 依赖注入:IoC模式
public class MvcEngine
{
public async Task StartAsync(Uri address)
{
await ListenAsync(address);
while (true)
{
var request = await ReceiveAsync();
var controller = await CreateControllerAsync(request);
var view = await ExecuteControllerAsync(controller);
await RenderViewAsync(view);
}
}
protected virtual Task ListenAsync(Uri address);
protected virtual Task<Request> ReceiveAsync();
protected virtual Task<Controller> CreateControllerAsync(Request request);
protected virtual Task<View> ExecuteControllerAsync(Controller controller);
protected virtual Task RenderViewAsync(View view);
}
[ASP.NET Core 3框架揭秘] 依赖注入:IoC模式

对于具体的应用程序来说,如果定义在MvcEngine中针对请求的处理方式完全符合要求,它只需要创建一个MvcEngine对象,然后指定一个监听地址调用模板方法StartAsync开启这个MVC引擎即可。如果该MVC引擎处理请求的某个环节不能满足要求,我们可以创建MvcEngine的派生类,并重写实现该环节的相应虚方法即可。比如说定义在某个应用程序中的Controller都是无状态的,它希望采用单例(Singleton)的方式重用已经激活的Controller对象以提高性能,那么它就可以按照如下的方式创建一个自定义的FoobarMvcEngine并按照自己的方式重写CreateControllerAsync方法即可。

[ASP.NET Core 3框架揭秘] 依赖注入:IoC模式
public class FoobarMvcEngine : MvcEngine
{
protected override Task<View> CreateControllerAsync (Request request)
{
<<省略实现>>
}
}
[ASP.NET Core 3框架揭秘] 依赖注入:IoC模式

二、工厂方法

对于一个复杂的流程来说,我们倾向于将组成该流程的各个环节实现在相应的组件之中,那么针对流程的定制就可以通过提供相应组件的形式来实现。我们知道23种设计模式之中有一种重要的类型,那就是“创建型模式”,比如常用的“工厂方法”和“抽象工厂”,IoC所体现的针对流程的共享与定制同样可以通过这些设计模式来完成。

所谓的工厂方法,说白了就是在某个类中定义用来提供所需服务对象的方法,这个方法可以是一个单纯的虚方法,也可以是具有默认实现的虚方法。至于方法声明的返回类型,可以是一个接口或者抽象类,也可以是未封闭(Sealed)的具体类型。作为它的派生类型,可以实现或者重写工厂方法以提供所需的服务对象。

同样以我们的MVC框架为例,我们让独立的组件来完成整个请求处理流程的几个核心环节。具体来说,我们为这些核心组件定义了如下几个对应的接口。IWebListener接口用来监听、接收和响应请求(针对请求的响应由ReceiveAsync方法返回的HttpContext上下文来完成),IControllerActivator接口用于根据当前HttpContext上下文激活目标Controller对象,并在Controller对象执行后做一些释放回收工作。至于IControllerExecutor和IViewRender接口则分别用来完成针对Controller的执行和针对View的呈现。

[ASP.NET Core 3框架揭秘] 依赖注入:IoC模式
public interface IWebListener
{
Task ListenAsync(Uri address);
Task<HttpContext> ReceiveAsync();
} public interface IControllerActivator
{
Task<Controller> CreateControllerAsync(HttpContext httpContext);
Task ReleaseAsync(Controller controller);
} public interface IControllerExecutor
{
Task<View> ExecuteAsync(Controller controller, HttpContext httpContext);
} public interface IViewRender
{
Task RendAsync(View view, HttpContext httpContext);
}
[ASP.NET Core 3框架揭秘] 依赖注入:IoC模式

我们在作为MVC引擎的MvcEngine中定义了四个工厂方法(GetWebListener、GetControllerActivator、GetControllerExecutor和GetViewRenderer)来提供上述这四种组件。这四个工厂方法均为具有默认实现的虚方法,我们可以利用它们提供默认的组件。在用于启动引擎的StartAsync方法中,我们利用这些工厂方法提供的对象来具体完成整个请求处理流程。

[ASP.NET Core 3框架揭秘] 依赖注入:IoC模式
public class MvcEngine
{
public async Task StartAsync(Uri address)
{
var listener = GetWebListener();
var activator = GetControllerActivator();
var executor = GetControllerExecutor();
var render = GetViewRender(); await listener.ListenAsync(address);
while (true)
{
var httpContext = await listener.ReceiveAsync();
var controller = await activator.CreateControllerAsync(httpContext);
try
{
var view = await executor.ExecuteAsync(controller, httpContext);
await render.RendAsync(view, httpContext);
}
finally
{
await activator.ReleaseAsync(controller);
}
}
}
protected virtual IWebLister GetWebListener();
protected virtual IControllerActivator GetControllerActivator();
protected virtual IControllerExecutor GetControllerExecutor();
protected virtual IViewRender GetViewRender();
}
[ASP.NET Core 3框架揭秘] 依赖注入:IoC模式

对于具体的应用程序来说,如果需要对请求处理的某个环节进行定制,它需要将定制的操作实现在对应接口的实现类中。在MvcEngine的派生类中,我们需要重写对应的工厂方法来提供被定制的对象即可。 比如上面提及的以单例模式提供目标Controller对象的实现就定义在SingletonControllerActivator类中,我们在派生于MvcEngine的FoobarMvcEngine类中重写了工厂方法GetControllerActivator使其返回一个SingletonControllerActivator对象。

[ASP.NET Core 3框架揭秘] 依赖注入:IoC模式
public class SingletonControllerActivator : IControllerActivator
{
public Task<Controller> CreateControllerAsync(HttpContext httpContext)
{
<<省略实现>>
}
public Task ReleaseAsync(Controller controller) => Task.CompletedTask;
} public class FoobarMvcEngine : MvcEngine
{
protected override ControllerActivator GetControllerActivator() => new SingletonControllerActivator();
}
[ASP.NET Core 3框架揭秘] 依赖注入:IoC模式

三、抽象工厂

虽然工厂方法和抽象工厂均提供了一个“生产”对象实例的工厂,但是两者在设计上却有本质的不同。工厂方法利用定义在某个类型的抽象方法或者虚方法完成了针对“单一对象”的提供,而抽象工厂则利用一个独立的接口或者抽象类来提供“一组相关的对象”。

具体来说,我们需要定义一个独立的工厂接口或者抽象工厂类,并在其中定义多个工厂方法来提供“同一系列”的多个相关对象。如果希望抽象工厂具有一组默认的“产出”,我们也可以将一个未被封闭类型作为抽象工厂,以虚方法形式定义的工厂方法将默认的对象作为返回值。在具体的应用开发中,我们可以通过实现工厂接口或者继承抽象工厂类(不一定是抽象类)的方式来定义具体工厂类,并利用它来提供一组定制的对象系列。

现在我们采用抽象工厂模式来改造我们的MVC框架。如下面的代码片段所示,我们定义了一个名为IMvcEngineFactory的接口作为抽象工厂,并在其中定义了四个方法来提供请求监听和处理过程使用到的四种核心对象。如果MVC提供了针对这四种核心组件的默认实现,我们可以按照如下的方式为这个抽象工厂提供一个默认实现(MvcEngineFactory)。

[ASP.NET Core 3框架揭秘] 依赖注入:IoC模式
public interface IMvcEngineFactory
{
IWebLister GetWebListener();
IControllerActivator GetControllerActivator();
IControllerExecutor GetControllerExecutor();
IViewRender GetViewRender();
} public class MvcEngineFactory: IMvcEngineFactory
{
public virtual IWebLister GetWebListener();
public virtual IControllerActivator GetControllerActivator();
public virtual IControllerExecutor GetControllerExecutor();
public virtual IViewRender GetViewRender();
}
[ASP.NET Core 3框架揭秘] 依赖注入:IoC模式

现在我们采用抽象工厂模式来改造我们的MVC框架。我们在创建MvcEngine对象的时候提供一个具体的IMvcEngineFactory对象,如果没有显式指定,MvcEngine会默认使用EngineFactory对象。在用于启动引擎的StartAsync方法中,MvcEngine利用IMvcEngineFactory对象来获取相应的对象来完成对请求的处理流程。

[ASP.NET Core 3框架揭秘] 依赖注入:IoC模式
public class MvcEngine
{
public IMvcEngineFactory EngineFactory { get; }
public MvcEngine(IMvcEngineFactory engineFactory = null) => EngineFactory = engineFactory ?? new MvcEngineFactory(); public async Task StartAsync(Uri address)
{
var listener = EngineFactory.GetWebListener();
var activator = EngineFactory.GetControllerActivator();
var executor = EngineFactory.GetControllerExecutor();
var render = EngineFactory.GetViewRender(); await listener.ListenAsync(address);
while (true)
{
var httpContext = await listener.ReceiveAsync();
var controller = await activator.CreateControllerAsync(httpContext);
try
{
var view = await executor.ExecuteAsync(controller, httpContext);
await render.RendAsync(view, httpContext);
}
finally
{
await activator.ReleaseAsync(controller);
}
}
}
}
[ASP.NET Core 3框架揭秘] 依赖注入:IoC模式

如果具体的应用程序需要采用前面定义的SingletonControllerActivator以单例的模式来激活目标Controller对对象,可以按照如下的方式定义一个具体的工厂类FoobarEngineFactory。最终的应用程序将利用这个FoobarEngineFactory对象来创建作为引擎的MvcEngine对象即可。

[ASP.NET Core 3框架揭秘] 依赖注入:IoC模式
public class FoobarEngineFactory : EngineFactory
{
public override ControllerActivator GetControllerActivator()
{
return new SingletonControllerActivator();
}
} public class App
{
static async Task Main()
{
var address = new Uri("http://0.0.0.0:8080/mvcapp");
var engine = new MvcEngine(new FoobarEngineFactory());
await engine.StartAsync(address);
...
}
}
[ASP.NET Core 3框架揭秘] 依赖注入:IoC模式

[ASP.NET Core 3框架揭秘] 依赖注入:控制反转
[ASP.NET Core 3框架揭秘] 依赖注入:IoC模式
[ASP.NET Core 3框架揭秘] 依赖注入:依赖注入模式
[ASP.NET Core 3框架揭秘] 依赖注入:一个迷你版DI框架

[ASP.NET Core 3框架揭秘] 依赖注入:IoC模式的更多相关文章

  1. &lbrack;ASP&period;NET Core 3框架揭秘&rsqb; 依赖注入:控制反转

    ASP.NET Core框架建立在一些核心的基础框架之上,这些基础框架包括依赖注入.文件系统.配置选项和诊断日志等.这些框架不仅仅是支撑ASP.NET Core框架的基础,我们在进行应用开发的时候同样 ...

  2. &lbrack;ASP&period;NET Core 3框架揭秘&rsqb; 依赖注入&lbrack;5&rsqb;&colon; 利用容器提供服务

    毫不夸张地说,整个ASP.NET Core框架是建立在依赖注入框架之上的.ASP.NET Core应用在启动时构建管道以及利用该管道处理每个请求过程中使用到的服务对象均来源于依赖注入容器.该依赖注入容 ...

  3. &lbrack;ASP&period;NET Core 3框架揭秘&rsqb; 依赖注入&lbrack;8&rsqb;:服务实例的生命周期

    生命周期决定了IServiceProvider对象采用怎样的方式提供和释放服务实例.虽然不同版本的依赖注入框架针对服务实例的生命周期管理采用了不同的实现,但总的来说原理还是类似的.在我们提供的依赖注入 ...

  4. &lbrack;ASP&period;NET Core 3框架揭秘&rsqb; 依赖注入&lbrack;10&rsqb;:与第三方依赖注入框架的适配

    .NET Core具有一个承载(Hosting)系统,承载需要在后台长时间运行的服务,一个ASP.NET Core应用仅仅是该系统承载的一种服务而已.承载系统总是采用依赖注入的方式来消费它在服务承载过 ...

  5. &lbrack;ASP&period;NET Core 3框架揭秘&rsqb; 依赖注入&lbrack;9&rsqb;:实现概述

    <服务注册>.<服务消费>和<生命周期>主要从实现原理的角度对.NET Core的依赖注入框架进行了介绍,接下来更进一步,看看该框架的总体设计和实现.在过去的多个版 ...

  6. &lbrack;ASP&period;NET Core 3框架揭秘&rsqb; 依赖注入&lbrack;7&rsqb;:服务消费

    包含服务注册信息的IServiceCollection集合最终被用来创建作为依赖注入容器的IServiceProvider对象.当需要消费某个服务实例的时候,我们只需要指定服务类型调用IService ...

  7. &lbrack;ASP&period;NET Core 3框架揭秘&rsqb; 依赖注入&lbrack;6&rsqb;:服务注册

    通过<利用容器提供服务>我们知道作为依赖注入容器的IServiceProvider对象是通过调用IServiceCollection接口的扩展方法BuildServiceProvider创 ...

  8. &lbrack;ASP&period;NET Core 3框架揭秘&rsqb; 依赖注入&lbrack;4&rsqb;:一个Mini版的依赖注入框架

    在前面的章节中,我们从纯理论的角度对依赖注入进行了深入论述,我们接下来会对.NET Core依赖注入框架进行单独介绍.为了让读者朋友能够更好地理解.NET Core依赖注入框架的设计与实现,我们按照类 ...

  9. &lbrack;ASP&period;NET Core 3框架揭秘&rsqb; 依赖注入&lbrack;3&rsqb;:依赖注入模式

    IoC主要体现了这样一种设计思想:通过将一组通用流程的控制权从应用转移到框架之中以实现对流程的复用,并按照"好莱坞法则"实现应用程序的代码与框架之间的交互.我们可以采用若干设计模式 ...

  10. &lbrack;ASP&period;NET Core 3框架揭秘&rsqb; 依赖注入&lbrack;2&rsqb;:IoC模式

    正如我们在<依赖注入:控制反转>提到过的,很多人将IoC理解为一种"面向对象的设计模式",实际上IoC不仅与面向对象没有必然的联系,它自身甚至算不上是一种设计模式.一般 ...

随机推荐

  1. vue实例属性(vm&period;&dollar;els)

    不需要表达式 参数: id(必需) 用法: 为 DOM 元素注册一个索引,方便通过所属实例的 $els 访问这个元素. 注意: 因为 HTML 不区分大小写,camelCase 名字比如 v-el:s ...

  2. android srudio使用HttpClient

    最近学习Android网络编程,在AndroidStudio下无法使用HttpClient,在网上找到了答案在这里记下来: //官方原文Apache HTTP Client RemovalAndroi ...

  3. opencv中的&period;at方法

    opencv中的.at方法是用来获取图像像素值得函数: interpolation:差值 histogram:直方图

  4. Ext Js学习之IIS理解

    站点分为静态网站和动态网站,纯粹利用html编写的网站属于静态网站,不宜维护和更新而利用C#+extjs等前台+后台技术编写的网站就属于动态站点,有更多的交互,易维护和更新,比如降价的页面,利用htm ...

  5. 【POJ2631】Roads in the North 树的直径

    题目大意:给定一棵 N 个节点的边权无根树,求树的直径. 代码如下 #include <cstdio> #include <algorithm> using namespace ...

  6. 使用JQuery获取对象的几种方式(转)

    原文:http://51876.iteye.com/blog/1350358 1.先讲讲JQuery的概念 JQuery首先是由一个 America 的叫什么 John Resig的人创建的,后来又很 ...

  7. python基础--接口与归一化设计、封装、异常、网络编程

    1 接口与归一化设计 1.1 归一化概念: 归一化的好处: 1.归一化让使用者无需关心对象的类是什么,只需要知道这些对象都具备某些功能就可以了,这极大降低了使用者的使用难度. 2.归一化使得高层的外部 ...

  8. ztree3&period;5&period;02选中结点时报错

    ztree3.5.02选中结点时报错 更新jquery版本后,使用jquery1.1.1,左键点击Tree里的节点的时候报错,虽然不影响显示,但是在chrome控制台显示报错 Uncaught Typ ...

  9. CSS3新特性回顾

    CSS3 介绍 开始实例 新特征简介 强大的CSS选择器 抛弃图片的视觉效果 盒模型变化(多列布局和弹性盒模型) 阴影效果 Web字体和web Font 图标 CSS33过渡与动画交互效果 媒体查询 ...

  10. hdu 3336 Count the string(next数组)

    题意:统计前缀在串中出现的次数 思路:next数组,递推 #include<iostream> #include<stdio.h> #include<string.h&g ...