控制反转Inversion of Control

DI和IoC几乎都是成对出现的,我们在理解依赖注入之前首先要弄明白什么是IoC,也就是控制反转,体现的就是控制权的转移,即控制权原来在A中,现在需要B来接管。那么在软件中是如何实现的。通过一个例子来说明传统设计在采用IoC之后是如何实现反转的。

我们模拟了一个http请求流程,由其中5个核心任务组成。

namespace IoCDemo
{
    class Program
    {
        static async Task Main()
        {
            while (true)
            {
                var address = new Uri("http://127.0.0.1:8080/api/test");
                await MvcLib.ListenAsync(address);
                while (true)
                {
                    var request = await MvcLib.ReceiceAsync();
                    var controller = await MvcLib.CreateControllerAsync(request);
                    var view = await MvcLib.ExecuteControllerAsync(controller);
                    await MvcLib.RenderViewAsync(view);
                }
            }
        }
    }
    public static class MvcLib
    {
        /// <summary>
        /// 启动一个监听器并将其绑定到指定的地址进行http请求的监听
        /// </summary>
        /// <param name="address"></param>
        /// <returns></returns>
        public static Task ListenAsync(Uri address)
        {
            return Task.CompletedTask;
        }
        /// <summary>
        /// 接收抵达的请求
        /// </summary>
        /// <returns>接收到的请求</returns>
        public static Task<HttpContext> ReceiceAsync()
        {
            return Task.FromResult(new HttpContext());
        }
        /// <summary>
        /// 根据接收的请求解析并激活目标Controller对象
        /// </summary>
        /// <param name="request"></param>
        /// <returns>激活的Controller对象</returns>
        public static Task<Controller> CreateControllerAsync(HttpContext request)
        {
            return Task.FromResult(new Controller());
        }
        /// <summary>
        /// 执行激活的对象Controller,返回视图
        /// </summary>
        /// <param name="controller"></param>
        /// <returns>表示视图的对象</returns>
        public static Task<View> ExecuteControllerAsync(Controller controller)
        {
            return Task.FromResult(new View());
        }
        /// <summary>
        /// 将视图装换成html请求
        /// </summary>
        /// <param name="view"></param>
        /// <returns></returns>
        public static Task RenderViewAsync(View view)
        {
            return Task.CompletedTask;
        }
    }
    public class View
    {
    }
    public class Controller
    {
    }
    public class HttpContext
    {
    }
}

上面的代码中,类库MvcLib仅仅通过api的形式提供单一功能的实现,http请求的工作流是在应用程序中进行了实现。但是所有的http请求都是这个工作流,也就是说这个工作流没有得到重用。那么我们可以通过一个框架(Framework)来实现工作流,以达到工作流复用的目的。那么应用程序只需要复用框架即可。这里面就是将对工作流的控制放到了框架中,而不是应用程序自己来控制,这个过程其实就是将工作流的控制权从应用程序交到了框架中,也就实现了控制反转。

如果我们将一个工作流程定义在框架中(A→B→C),建立在框架基础上的两个应用程序App1和App2需要对这个工作流进行自定义。框架驱动工作流自动运行,并且会按照运行前自定义好的工作流内容进行执行。

综上,IoC一方面通过流程控制从应用程序向框架反转,实现了针对流程的重用,另一方面通过内置的扩展机制是这个被重用的流程能够自由的被定制。

IoC模式

IoC被视为一种设计原则,在很多设计模式中都有所体现。

模板方法

将一个可复用的工作流程或者由多个步骤组成的算法定义成模板方法,组成这个流程或者算法的单一步骤则在响应的虚方法中实现,模板方法根据预先编排的流程调用这些虚方法。这些方法均定义在一个类中,可以通过派生类并重写响应的虚方法的方式达到对流程定制的目的。

#region 模板方法
/// <summary>
/// 将整个执行流程放到类中,在类中分别定义了5个虚方法。
/// 模板方法StartAsync根据预定义的请求处理流程先后调用这5个方法。
/// 在具体的应用场景中,只需要实例化MvcEngine,并调用StartAsync方法就可以满足基本要求
/// </summary>
public class MvcEngine
{
    public async Task StartAsync(Uri address)
    {
        await ListenAsync(address);
        while (true)
        {
            var request = await ReceiceAsync();
            var controller = await CreateControllerAsync(request);
            var view = await ExecuteControllerAsync(controller);
            await RenderViewAsync(view);
        }
    }     protected virtual Task ListenAsync(Uri address) => Task.CompletedTask;
    protected virtual Task<HttpContext> ReceiceAsync() => Task.FromResult(new HttpContext());
    protected virtual Task<Controller> CreateControllerAsync(HttpContext request) => Task.FromResult(new Controller());
    protected virtual Task<View> ExecuteControllerAsync(Controller controller) => Task.FromResult(new View());
    protected virtual Task RenderViewAsync(View view) => Task.CompletedTask;
}
/// <summary>
/// 如果请求环节无法满足应用场景,可以创建派生类,并重写某个环节的虚方法即可。
/// </summary>
public class FoobarMvcEngine : MvcEngine
{
    protected override Task<Controller> CreateControllerAsync(HttpContext request)
    {
        //此处省略了扩展实现
        
        return Task.FromResult(new Controller());
    }
}
#endregion

工厂方法

对于一个复杂的流程,可以将组成流程的各个环节实现在响应的组件之中,所以针对流程的定制可以通过提供响应的组件的形式来实现。工厂方法和抽象工厂都可以实现。

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

针对上面的MVC框架流程,将整个请求处理流程独立成几个核心环节,核心环节对应不同的接口。

#region 工厂方法

/// <summary>
/// 监听、接收和响应请求
/// </summary>
public interface IWebListener
    {
        Task ListenAsync(Uri address);
        Task<HttpContext> ReceiceAsync();
    }
public class WebListener : IWebListener
    {
        public Task ListenAsync(Uri address) => Task.CompletedTask;
        public Task<HttpContext> ReceiceAsync() => Task.FromResult(new HttpContext());
    } /// <summary>
/// 根据当前上下文激活目标Controller对象,并做一些释放回收的工作
/// </summary>
public interface IControllerActivator
    {
        Task<Controller> CreateControllerAsync(HttpContext request);
        Task ReleaseAsync(Controller controller);
    }
public class ControllerActivator : IControllerActivator
    {
        public Task<Controller> CreateControllerAsync(HttpContext request) => Task.FromResult(new Controller());
        public Task ReleaseAsync(Controller controller) => Task.CompletedTask;
    } /// <summary>
/// 针对Controller的执行
/// </summary>
public interface IControllerExecutor
    {
        Task<View> ExecuteAsync(Controller controller);
    }
public class ControllerExecutor : IControllerExecutor
    {
        public Task<View> ExecuteAsync(Controller controller) => Task.FromResult(new View());
    } /// <summary>
/// 视图的呈现
/// </summary>
public interface IViewRender
    {
        Task RenderViewAsync(View view);
    }
public class ViewRender : IViewRender
    {
        public Task RenderViewAsync(View view) => Task.CompletedTask;
    } /// <summary>
/// 采用默认的实现,实现预定的流程
/// </summary>
public class MvcEngine1
    {
        public async Task StartAsync(Uri address)
        {
            var listener = GetWebListener();
            var controllerActivator = GetControllerActivator();
            var controllerExecutor = GetControllerExecutor();
            var viewRender = GetIViewRender();             await listener.ListenAsync(address);
            while (true)
            {
                var request = await listener.ReceiceAsync();
                var controller = await controllerActivator.CreateControllerAsync(request);
                try
                {
                    var view = await controllerExecutor.ExecuteAsync(controller);
                    await viewRender.RenderViewAsync(view);
                }
                finally
                {
                    await controllerActivator.ReleaseAsync(controller);
                }             }
        }         protected virtual IWebListener GetWebListener() => new WebListener();
        protected virtual IControllerActivator GetControllerActivator() => new ControllerActivator();
        protected virtual IControllerExecutor GetControllerExecutor() => new ControllerExecutor();
        protected virtual IViewRender GetIViewRender() => new ViewRender();
    } /// <summary>
/// 定制化的IControllerActivator流程
/// 需要根据需要实现接口
/// </summary>
public class ControllerExActivator : IControllerActivator
    {
        public Task<Controller> CreateControllerAsync(HttpContext request)
        {
            //省略实现             return Task.FromResult(new Controller());
        }
        public Task ReleaseAsync(Controller controller)
        {
            //省略实现             return Task.CompletedTask;
        }
    } /// <summary>
/// 如果请求环节无法满足应用场景,可以创建派生类,并重写某个环节的工厂方法即可。
/// </summary>
public class FoobarMvcEngine1 : MvcEngine1
    {
        protected override IControllerActivator GetControllerActivator()
        {
            return new ControllerExActivator();
        }
    } #endregion

抽象工厂

工厂方法和抽象工厂都能够生产对象实例,但是两者本质上有区别。工厂方法利用定义在某个类型的抽象方法或者虚方法完成针对单一对象的提供,而抽象工厂是利用一个独立的接口或者抽象类提供一组相关的对象

定义了一个独立的工厂接口或者抽象工厂类,并在其中定义多个工厂方法来提供多个相关对象。如果希望抽象工厂具有一组默认的输出,可以将一个未封闭的类型作为抽象工厂,以虚方法的形式定义默认实现来返回对象。在具体的开发中,可以实现工厂接口或者继承抽象工厂类,来实现具体的工厂类,然后提供一系列对象。

#region 抽象工厂

/// <summary>
/// 抽象工厂会创建多个实例
/// </summary>
public interface IMvcEngineFactory
    {
        IWebListener GetWebListener();
        IControllerActivator GetControllerActivator();
        IControllerExecutor GetControllerExecutor();
        IViewRender GetIViewRender();
    }
/// <summary>
/// 默认实现
/// </summary>
public class MvcEngineFactory : IMvcEngineFactory
    {
        public virtual IWebListener GetWebListener() => new WebListener();
        public virtual IControllerActivator GetControllerActivator() => new ControllerActivator();
        public virtual IControllerExecutor GetControllerExecutor() => new ControllerExecutor();
        public virtual IViewRender GetIViewRender() => new ViewRender();
    } /// <summary>
/// 采用默认的实现,实现预定的流程
/// </summary>
public class MvcEngine2
    {
        public IMvcEngineFactory _engineFactory { get; }         public MvcEngine2(IMvcEngineFactory engineFactory)
        {
            _engineFactory = engineFactory ?? new MvcEngineFactory();
        }         public async Task StartAsync(Uri address)
        {
            var listener = _engineFactory.GetWebListener();
            var controllerActivator = _engineFactory.GetControllerActivator();
            var controllerExecutor = _engineFactory.GetControllerExecutor();
            var viewRender = _engineFactory.GetIViewRender();             await listener.ListenAsync(address);
            while (true)
            {
                var request = await listener.ReceiceAsync();
                var controller = await controllerActivator.CreateControllerAsync(request);
                try
                {
                    var view = await controllerExecutor.ExecuteAsync(controller);
                    await viewRender.RenderViewAsync(view);
                }
                finally
                {
                    await controllerActivator.ReleaseAsync(controller);
                }             }
        }
    } /// <summary>
/// 如果请求环节无法满足应用场景,可以创建派生类,并重写某个环节的虚方法即可。
/// </summary>
public class FoobarMvcEngineFactory : MvcEngineFactory
    {
        public override IControllerActivator GetControllerActivator()
        {
            return new ControllerExActivator();
        }
    } #endregion

03 依赖注入--01控制反转、IoC模式的更多相关文章

  1. 依赖注入 DI 控制反转 IOC MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  2. ADO.NET .net core2.0添加json文件并转化成类注入控制器使用 简单了解 iTextSharp实现HTML to PDF ASP.NET MVC 中 Autofac依赖注入DI 控制反转IOC 了解一下 C# AutoMapper 了解一下

    ADO.NET   一.ADO.NET概要 ADO.NET是.NET框架中的重要组件,主要用于完成C#应用程序访问数据库 二.ADO.NET的组成 ①System.Data  → DataTable, ...

  3. 依赖注入 DI 控制反转 IOC 概念 案例 MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  4. 浅析“依赖注入(DI)/控制反转(IOC)”的实现思路

    开始学习Spring的时候,对依赖注入(DI)——也叫控制反转(IOC)—— 的理解不是很深刻.随着学习的深入,也逐渐有了自己的认识,在此记录,也希望能帮助其他入门同学更深入地理解Spring.本文不 ...

  5. ASP.NET MVC 中 Autofac依赖注入DI 控制反转IOC 了解一下

    先简单了解一这个几个 名词的意思. 控制反转(IOC) 依赖注入(DI) 并不是某种技术. 而是一种思想.一种面向对象编程法则 什么是控制反转(IOC)?  什么是依赖注入(DI) 可以点击下面链接 ...

  6. ASP.NET中IOC容器Autofac(依赖注入DI 控制反转IOC)

    IOC的一个重点是在程序运行中,动态的向某个对象提供它所需要的其他对象.这一点是通过DI来实现的.Autofac则是比较流行的一款IOC容器. IoC和DI有什么关系呢?其实它们是同一个概念的不同角度 ...

  7. 轻松学,浅析依赖倒置(DIP)、控制反转(IOC)和依赖注入(DI) 依赖注入和控制反转的理解,写的太好了。

    轻松学,浅析依赖倒置(DIP).控制反转(IOC)和依赖注入(DI) 2017年07月13日 22:04:39 frank909 阅读数:14269更多 所属专栏: Java 反射基础知识与实战   ...

  8. 浅谈(IOC)依赖注入与控制反转(DI)

    前言:参考了百度文献和https://www.cnblogs.com/liuqifeng/p/11077592.html以及http://www.cnblogs.com/leoo2sk/archive ...

  9. .NET Core的依赖注入[1]: 控制反转

    写在前面:我之前写过一系列关于.NET Core依赖注入的文章,由于.NET Core依赖注入框架的实现原理发生了很大的改变,加上我对包括IoC和DI这些理论层面的东西又有了一些新的理解,所以我在此基 ...

随机推荐

  1. "image watch" for QtCreator

    Image Watch Image Watch 是Visual Studio的一个插件,用来在C++ 调试时显示内存中的位图图像.可以直观的看到图像的变化而不用添加额外的显示代码.其内建了对OpenC ...

  2. Nacos 服务注册的原理

    Nacos 服务注册需要具备的能力: 服务提供者把自己的协议地址注册到Nacos server 服务消费者需要从Nacos Server上去查询服务提供者的地址(根据服务名称) Nacos Serve ...

  3. 【转】关于DNS不得不说的一些事

    转自:https://www.cnblogs.com/rjzheng/p/11395695.html 引言 今天我们来聊聊DNS.所谓域名系统(Domain Name System缩写DNS,Doma ...

  4. 利用AOP切面打印项目中每个接口的运行情况

    1.前言 AOP切面技术,大家应该都听知道,Spring框架的主要功能之一. AOP切面的用途很广,其中一个常见的用途就是打印接口方法的运行日志和运行时间. 日志对于一个项目很是重要,不仅有助于调错, ...

  5. go语言内存对齐

    内存对齐 为保证程序顺利高效的运行,编译器会把各种类型的数据安排到合适的地址并占用合适的长度,这就是内存对齐 每种类型的对齐值就是他的内存边界 64位 类型 对齐边界 (对齐值) int8 1byte ...

  6. MySQL5.7.24 安装

    官网地址 https://dev.mysql.com/downloads/mysql/ 1. 安装依赖 yum install -y cmake make gcc gcc-c++ libaio ncu ...

  7. Centos8.3安装broadcom(博通)BCM43142无线网卡驱动,Centos8没有wifi选项(No wifi adapter found centos)解决办法

    参考:杆菌大祭司> https://www.jianshu.com/p/3cb41b7b8fec 第一步:查看网卡型号,确认无线网卡型号为BCMXXX lspci | grep Network ...

  8. 使用HttpRunner3+Allure+Jenkins实现Web接口自动化测试

    陆续给不同项目做了Web接口自动化测试,在尝试不同方法的同时会有新的体会.最近用到了HttpRunner3,本文将记录使用HttpRunner3+Allure+Jenkins在项目中快速实现Web接口 ...

  9. [考试总结]noip模拟45

    真开心,挂没了.. 考完:"你们怎么第二题打了这么点分,明明一个爆搜就有65pts!!!怎么跟别人打?!" 然后我看了看我的爆搜,30pts. 然后认为自己打爆了... 我又想为什 ...

  10. Junit5快速入门指南-3

    Fixtures 是测试中非常重要的一部分.他们的主要目的是建立一个固定/已知的环境状态以确保 测试可重复并且按照预期的方式运行.比如在app测试中 基类@BeforeClass 配置初始化,初始化d ...