控制反转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. 解决springboot在mac电脑下启动过慢的问题

    自从用了mac以后,springboot启动的时候一直卡在build环节10多秒 但是在linux和Windows环境下,启动只要6秒,后面查看了一下其他 人也遇到这种问题,原来是需要在hosts文件 ...

  2. 【多线程】不懂什么是 Java 中的锁?看看这篇你就明白了!

    本文来源:Java建设者 原文地址:https://mp.weixin.qq.com/s/GU42BjM5jY2CEMVD_PAZBQ Java 锁分类 Java 中的锁有很多,可以按照不同的功能.种 ...

  3. QT 自定义控件 以及信号和槽的使用

    自定义login 控件 Login头文件 #ifndef LOGIN_H #define LOGIN_H #include <QWidget> namespace Ui { class L ...

  4. BootStrap学习代码

    要为毕设做准备了! 哎,毕设前台得自己来,所以打算学学bootstrap,把学习的代码放到码云上面了,使用HbuilderX来写,界面友好,适合我这种前端小白- 第一天就感受到了写html快捷键的强大 ...

  5. C++类和对象笔记

    笔记参考C++视频课程 黑马C++ C++ 面向对象的三大特性:封装.继承.多态 目录 目录 目录 一.封装 1.1 封装的意义-属性和行为 1.2 struct和class的区别 1.3 成员属性设 ...

  6. SpringSecurity入门

    基础 spring security的底层就是一个过滤器链 ExceptionTranslationFilter是一个异常过滤器,用来处理认证授权过程中的异常 UseranmePasswordAuth ...

  7. VSCode添加某个插件后,Python 运行时出现Segmentation fault (core dumped) 解决办法

    在VSCode添加某个插件后,Debug出现Segmentation fault (core dumped) 解决方案,在当前environment下运行: conda update --all

  8. Linux centos7 scp命令

    1. 命令简介 scp(secure copy) 是 linux 系统下基于 ssh 登陆进行安全的远程文件拷贝命令,可以在两台 Linux 主机进行复制操作 # 语法 scp [-1246BCpqr ...

  9. Mysql - You can't specify target table '表名' for update in FROM clause 错误解决办法

    背景 在MySQL中,写SQL语句的时候 ,可能会遇到 You can't specify target table '表名' for update in FROM clause 这样的错误 错误含义 ...

  10. ElasticSearch集成SpringData史上最全查询教程

    1.简单介绍 springboot 使用springdata操作es,ElasticsearchRepository使用QueryBuilder构造查询条件 2.集成es //maven集成 < ...