控制反转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. ThreadLocal, volatile, synchronized, map, epoll, AQS简单总结

    ThreadLocal ThreadLocal主要是为了解决内存泄漏的问题,它是一种弱引用: 引用总共有四种,,我简单列一下: 强引用(Strong Reference):正常引用,根据垃圾回收算法, ...

  2. Mysql 中隐式转换

    案例一:条件字段函数操作 假设你现在维护了一个交易系统,其中交易记录表 tradelog 包含交易流水号(tradeid).交易员 id(operator).交易时间(t_modified)等字段.为 ...

  3. 关于Typora的使用

    关于Typora的使用 今天介绍一个做笔记用的一款markdown编辑器,用了之后,顺利被圈粉: Typora,美观小众,可用性强! 和传统的markdown编辑器不一样,传统的markdown编辑器 ...

  4. linux 常用命令(一)——查看硬盘空间-内存-线程的cpu负载-线程内存

    系统参数检查: df -h [enter] 检查硬盘空间 TIP: 使用 man df 可查看该命令使用说明 ; q 退出. free检查内存使用情况: free [enter] TIP: 使用 ma ...

  5. Struts2之国际化

    时间:2017-1-11 11:12 --国际化Struts2已经对国际化进行了封装,我们只需要根据其提供的API进行访问即可.要使用国际化的Action必须继承ActionSupport.1.什么是 ...

  6. Faiss使用多线程出现的性能问题

    Faiss使用多线程出现的性能问题 faiss在增加CPU的情况下,反而出现效率低下的问题. 从理论上看,作为一个CPU/GPU计算型的应用,更多的核意味着更大的计算吞吐能力,性能只会越来越好才是. ...

  7. 阿里云服务器,http免费转https详细教程

    1.搜ssl证书,点击立即购买 2.这里我们选择免费证书,点击右边立即购买,去支付 购买完成之后,申请证书状态会显示在审核中,不过很快的,几分钟就申请成功了,以下就是我申请成功的界面,因为我服务器用的 ...

  8. Windows系统一些好用的办公工具

    在日常办公过程中,总有一些工具令人觉得方便,提高了工作效率.以下是根据我的习惯,收集了一些好用的工具,在此记录且不定期更新. 文件名 说明 Everything 文件搜索工具,搜索速度快 ALTRun ...

  9. VS Code闪现,巨头纷纷入局的Web IDE缘何崛起?

    我发了,我装的. 就在前几天,微软简短的发布了Visual Studio Code for the Web 的公告,而没过一阵,这则公告就被删除了,现在点经相关内容已经是404状态了.虽然公告的内容已 ...

  10. PPP协议、PPPoE协议、L2TP协议的关系

    1. 简述 首先对这3中协议做一个简单的描述: 协议 协议类型 描述 PPP 点对点链路层协议 应用最广泛的点对点协议,可应用在多种网络,改善了SLIP协议的不足 PPPoE 点对点链路层协议 对PP ...