zoukankan      html  css  js  c++  java
  • 03 依赖注入--01控制反转、IoC模式

    控制反转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
    
    
  • 相关阅读:
    hdu 2795 Billboard
    serialusbfn.dll这个罪魁祸首搞得我郁闷之极(作者:wogoyixikexie@gliet)
    开发板买来后如何练习写驱动、开发BSP?(作者:wogoyixikexie@gliet)
    PB命令行——查看DLL导出函数
    【劲爆】发帖达到要求即可获得一块mini6410/OK6410开发板!
    GOD!我终于找到博客园设置博客文章图片签名的方法了!
    如何删除wince5.0控制面板的密码设置以及拨号等组件
    【原创】最值得推荐wince应用和wince驱动入门书籍
    gooogleman嵌入式联盟之图标初步设计进展
    2440 5.0BSP之OEMInterruptHandler函数
  • 原文地址:https://www.cnblogs.com/vigorous/p/13815658.html
Copyright © 2011-2022 走看看