控制反转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. wpf 实现印章,公章效果

    能写一些特定外观的控件,是一个做界面开发的程序员的基础技能.基本上,不管你是做web,QT,MFC,Winform,WPF等等,如果自己看到一个比较好看的有趣的效果,能大致推断出它的实现方式并照猫画虎 ...

  2. Linux下的Shell工作原理

    Linux下的Shell工作原理 Linux系统提供给用户的最重要的系统程序是Shell命令语言解释程序.它不属于内核部分,而是在核心之外,以用户态方式运行.其基本功能是解释并执行用户打入的各种命令, ...

  3. 初识javaScript(慕课网学习笔记)

    js输出 window.alert() 警告框 document.write() 写到HTML文档中 innerHTML 写到HTML元素 console.log() 写到浏览器的控制台 <!D ...

  4. Android开发,缺少权限导致无法修改原文件,获取所有文件访问权限的方法

    在Android 11开发中,app会遇到使用绝对路径无法打开某文件的情况(文件存在根目录下,获取到的路径为:/storage/emulated/0/XXX.txt),而使用相对路径打开文件后(获取到 ...

  5. webpack编译后的代码如何在浏览器执行

    浏览器是无法直接使用模块之间的commonjs或es6,webpack在打包时做了什么处理,才能让浏览器能够执行呢,往下看吧. 使用commonjs语法 先看下写的代码, app.js minus.j ...

  6. 小程序 mpvue page "xxx" has not been registered yet

    新增了几个页面,改了下目录结构,就开始报这个错. 重启了几次不管用,google 一番也无果. 灵机一动试一下 build npm run build build 版本没报错,OK 然后 $ rm - ...

  7. H5、C3基础知识笔记

    HTML5 本文内容参考于"HTML5|W3scool"教程 简介 是最新的 HTML 标准,拥有新的语义.图形以及多媒体元素 提供了新的 API 简化了 web 应用程序的搭建 ...

  8. WebService学习总结(五)--CXF的拦截器

    拦截器是Cxf的基础,Cxf中很多的功能都是由内置的拦截器来实现的,拦截器在Cxf中由Interceptor表示.拦截器的作用类似axis2中handle.Cxf的拦截器包括入拦截器和出拦截器,所有的 ...

  9. NOIP模拟21:「Median·Game·Park」

    T1:Median   线性筛+桶+随机化(??什么鬼?).   首先,题解一句话秀到了我: 考虑输入如此诡异,其实可以看作随机数据   随机数据??   这就意味着分布均匀..   又考虑到w< ...

  10. outerHTML和outerText的赋值是异步的

    用JavaScript操作DOM时,经常有生成复杂HTML结构的需求.此时,通常不是用标准DOM接口(如createElement().setAttribute().append()等)来语句式地生成 ...