正如我们在《控制反转》提到过的,很多人将IoC理解为一种“面向对象的设计模式”,实际上IoC自身不仅与面向对象没有必然的联系,它也算不上是一种设计模式。一般来讲,设计模式提供了一种解决某种具体问题的方案,但是IoC既没有一个针对性的问题领域,其自身没有提供一种可实施的解决方案,所以我更加倾向于将IoC视为一种设计原则。实际上很多我们熟悉的设计模式背后采用了IoC原则,接下来我们就来介绍几种典型的“设计模式”。

一、模板方法

提到IoC,很多人首先想到的是DI,但是在我看来与IoC思想最为接近的倒是另一种被称为“模板方法(Template  Method)”的设计模式。模板方法模式与IoC的意图可以说不谋而合,该模式主张将一个可复用的工作流程或者由多个步骤组成的算法定义成模板方法,组成这个流程或者算法的步骤实现在相应的虚方法之中,模板方法根据按照预先编排的流程去调用这些虚方法。所有这些方法均定义在同一个类中,我们可以通过派生该类并重写相应的虚方法达到对流程定制的目的。

对于《控制反转》演示的这个MVC的例子,我们可以将整个请求处理流程实现在如下一个MvcEngine类中,请求的监听与接收、目标Controller的激活与执行以及View的呈现分别定义在5个受保护的虚方法中,模板方法StartAsync根据预定义的请求处理流程先后调用这5个方法。

public class MvcEngine
{public async Task StartAsync(Uri address){await ListenAsync(address);while (true){var request = await ReceiveAsync();var controller = await CreateControllerAsync(request);var view = await ExecuteControllerAsync(controller);await RenderViewAsync(view);}}protected virtual Task ListenAsync(Uri address);protected virtual Task<Request> ReceiveAsync();protected virtual Task<Controller> CreateControllerAsync(Request request);protected virtual Task<View> ExecuteControllerAsync(Controller controller);protected virtual Task RenderViewAsync(View view);
}

对于具体的应用来说,如果定义在MvcEngine针对请求的处理方式完全符合它的要求,它只需要创建这个一个MvcEngine对象,然后指定一个对应的基地址调用模板方法StartAsync开启这个MVC引擎即可。如果该MVC引擎处理请求的某个环节不能满足它的要求,它可以创建MvcEngine的派生类,并重写实现该环节的相应虚方法即可。

比如说定义在某个应用程序中的Controller都是无状态的,它希望采用单例(Singleton)的方式重用已经激活的Controller以提高性能,那么它就可以按照如下的方式创建一个自定义的FoobarMvcEngine并按照自己的方式重写

public class FoobarMvcEngine : MvcEngine
{protected override Task<View> CreateControllerAsync (Request request){<<省略实现>>}
}

二、工厂方法

对于一个复杂的流程来说,我们倾向于将组成该流程的各个环节实现在相对独立的组件之中,那么针对流程的定制就可以通过提供定制组件的方式来实现。我们知道23种设计模式之中有一种重要的类型,那就是“创建型模式”,比如常用的“工厂方法”和“抽象工厂”,IoC所体现的针对流程的共享与定制可以通过它们来完成。

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

同样以我们的MVC框架为例,我们让独立的组件来完成组成整个请求处理流程的几个核心环节。具体来说,我们针对这些核心组件定义了如下这几个对应的接口。IWebLister接口用来监听、接收和响应请求(针对请求的响应由ReceiveAsync方法返回的HttpContext对象来完成,后者表示针对当前请求的上下文),IControllerActivator接口用于根据当前请求激活目标Controller对象,已经在后者执行完成后做一些释放回收工作。至于IControllerExecutorIViewRender接口则分别用来完成针对Controller的执行和针对View的呈现。

public interface IWebLister
{Task ListenAsync(Uri address);Task<HttpContext> ReceiveAsync();
}public interface IControllerActivator
{Task<Controller> CreateControllerAsync(HttpContext httpContext);Task ReleaseAsync(Controller controller);
}public interface IControllerExecutor
{Task<View> ExecuteAsync(Controller controller, HttpContext httpContext);
}public interface IViewRender
{Task RendAsync(View view, HttpContext httpContext);
}

 

我们在作为MVC引擎的MvcEngine类中定义了四个工厂方法(GetWebListener、GetControllerActivator、GetControllerExecutor和GetViewRenderer)来提供上述这4种组件。这四个工厂方法均为具有默认实现的虚方法,我们可以利用它们提供默认的组件。在用于启动引擎的StartAsync方法中,我们利用这些工厂方法提供的对象来具体完成请求处理流程的各个核心环节。

public class MvcEngine
{public async Task StartAsync(Uri address){var listener = GetWebLister();var activator = GetControllerActivator();var executor = GetControllerExecutor();var render = GetViewRender();await listener.ListenAsync(address);while (true){var httpContext = await listener.ReceiveAsync();var controller = await activator.CreateControllerAsync(httpContext);try{var view = await executor.ExecuteAsync(controller, httpContext);await render.RendAsync(view, httpContext);}finally{await activator.ReleaseAsync(controller);}}}protected virtual IWebLister GetWebLister(); protected virtual IControllerActivator GetControllerActivator();protected virtual IControllerExecutor GetControllerExecutor();protected virtual IViewRender GetViewRender();
}

 

对于具体的应用程序来说,如果需要对请求处理的某个环节进行定制,它需要将定制的操作实现在对应接口的实现类中。在MvcEngine的派生类中,我们需要重写对应的工厂方法来提供被定制的对象。 比如上面提及的以单例模式提供目标Controller对象的实现就定义在SingletonControllerActivator类中,我们在派生于MvcEngine的FoobarMvcEngine类中重写了工厂方法GetControllerActivator使其返回一个SingletonControllerActivator对象。

public class SingletonControllerActivator : IControllerActivator
{         public Task<Controller> CreateControllerAsync(HttpContext httpContext){<<省略实现>>}public Task ReleaseAsync(Controller controller) => Task.CompletedTask;
}public class FoobarMvcEngine : MvcEngine
{protected override ControllerActivator GetControllerActivator() => new SingletonControllerActivator();
}

三、抽象工厂

虽然工厂方法和抽象工厂均提供了一个“生产”对象实例的工厂,但是两者在设计上却有本质的不同。工厂方法利用定义在某个类型的抽象方法或者虚方法实现了针对单一对象提供方式的抽象,而抽象工厂则利用一个独立的接口或者抽象类来提供一组相关的对象。

具体来说,我们需要定义一个独立的工厂接口或者抽象工厂类,并在其中定义多个的工厂方法来提供“同一系列”的多个相关对象。如果希望抽象工厂具有一组默认的“产出”,我们也可以将一个未被封闭的具体类作为抽象工厂,以虚方法形式定义的工厂方法将默认的对象作为返回值。我们根据实际的需要通过实现工厂接口或者继承抽象工厂类(不一定是抽象类)定义具体工厂类来提供一组定制的系列对象。

现在我们采用抽象工厂模式来改造我们的MVC框架。如下面的代码片段所示,我们定义了一个名为IMvcEngineFactory的接口作为抽象工厂,定义在其中定义了四个方法来提供请求监听和处理过程使用到的4种核心对象。如果MVC提供了针对这四种核心组件的默认实现,我们可以按照如下的方式为这个抽象工厂提供一个默认实现(MvcEngineFactory)。

public interface IMvcEngineFactory
{IWebLister GetWebLister();IControllerActivator GetControllerActivator();IControllerExecutor GetControllerExecutor();IViewRender GetViewRender();
}public class MvcEngineFactory: IMvcEngineFactory
{IWebLister GetWebLister();IControllerActivator GetControllerActivator();IControllerExecutor GetControllerExecutor();IViewRender GetViewRender();
}

 

现在我们采用抽象工厂模式来改造我们的MVC框架。我们在创建MvcEngine对象可以提供一个具体的IMvcEngineFactory对象,如果没有显式指定,MvcEngine会使用默认的EngineFactory对象。在用于启动引擎的StartAsync方法中,MvcEngine利用IMvcEngineFactory来获取相应的对象协作完整对请求的处理流程。

public class MvcEngine
{public IMvcEngineFactory EngineFactory { get; }public MvcEngine(IMvcEngineFactory engineFactory = null) => EngineFactory = engineFactory??new MvcEngineFactory();public async Task StartAsync(Uri address){var listener = EngineFactory.GetWebLister();var activator = EngineFactory.GetControllerActivator();var executor = EngineFactory.GetControllerExecutor();var render = EngineFactory.GetViewRender();await listener.ListenAsync(address);while (true){var httpContext = await listener.ReceiveAsync();var controller = await activator.CreateControllerAsync(httpContext);try{var view = await executor.ExecuteAsync(controller, httpContext);await render.RendAsync(view, httpContext);}finally{await activator.ReleaseAsync(controller);}}}}

 

如果具体的应用程序需要采用上面定义的SingletonControllerActivator以单例的模式来激活目标Controller,我们可以按照如下的方式定义一个具体的工厂类FoobarEngineFactory。最终的应用程序将这么一个FoobarEngineFactory对象作为MvcEngine的EngineFactory。

public class FoobarEngineFactory : EngineFactory
{public override ControllerActivator GetControllerActivator(){return new SingletonControllerActivator();}
}public class App
{static void Main(string[] args){Uri address = new Uri("http://0.0.0.0:8080/mvcapp");MvcEngine engine     = new MvcEngine(new FoobarEngineFactory());engine.Start(address);}
}

 

除了上面介绍这三种典型的设计,还有很多其他的设计模式,比如策略模式、观察者模式等等,它们无一不是采用IoC的设计原则。Martin Fowler在《Inversion of Control 》一文中正是通过观察者模式来介绍IoC的。我们将在下一篇中对依赖注入模式进行深入讲解。

依赖注入[1]: 控制反转
依赖注入[2]: 基于IoC的设计模式
依赖注入[3]: 依赖注入模式
依赖注入[4]: 创建一个简易版的DI框架[上篇]
依赖注入[5]: 创建一个简易版的DI框架[下篇]
依赖注入[6]: .NET Core DI框架[编程体验]
依赖注入[7]: .NET Core DI框架[服务注册]
依赖注入[8]: .NET Core DI框架[服务消费]

作者:蒋金楠
微信公众账号:大内老A
微博:www.weibo.com/artech
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

转载于:https://www.cnblogs.com/linybo/p/10053131.html

依赖注入[2]: 基于IoC的设计模式相关推荐

  1. java元婴期(18)----java进阶(spring(2)----DI(依赖注入)基于注解的IOC与DI配置properties)

    1.依赖注入的概念 依赖注入:Dependency Injection.它是spring框架核心ioc的具体实现. 我们的程序在编写时,通过控制反转,把对象的创建交给了spring,但是代码中不可能出 ...

  2. .NET平台依赖注入机制及IoC的设计与实现

    我们设计的分层架构,层与层之间应该是松散耦合的.因为是单向单一调用,所以,这里的"松散耦合"实际是指上层类不能具体依赖于下层类,而应该依赖于下层提供的一个接口.这样,上层类不能直接 ...

  3. 依赖注入[5]: 创建一个简易版的DI框架[下篇]

    为了让读者朋友们能够对.NET Core DI框架的实现原理具有一个深刻而认识,我们采用与之类似的设计构架了一个名为Cat的DI框架.在<依赖注入[4]: 创建一个简易版的DI框架[上篇]> ...

  4. AspectCore中的IoC容器和依赖注入

    IOC模式和依赖注入是近年来非常流行的一种模式,相信大家都不陌生了,在Asp.Net Core中提供了依赖注入作为内置的基础设施,如果仍不熟悉依赖注入的读者,可以看看由我们翻译的Asp.Net Cor ...

  5. 设计模式--谈谈IoC、DI、DIP、IoC Container、控制反转与依赖注入

    1. 介绍 控制反转(IoC).依赖反转原理(DIP).依赖注入(DI)和IoC容器这些术语你都清楚嘛? 2. 概念讲解 2.1. IoC 控制反转(IoC):是一种设计模式,用于在面向对象的设计中反 ...

  6. Java依赖注入 - DI设计模式示例教程

    Java依赖注入 - DI设计模式示例教程 Java依赖注入 设计模式允许我们删除硬编码的依赖项,并使我们的应用程序松散耦合,可扩展和可维护.我们可以在java中实现依赖注入,以将依赖项解析从编译时移 ...

  7. Spring高手之路——深入理解与实现IOC依赖查找与依赖注入

    本文从xml开始讲解,注解后面给出 文章目录 1. 一个最基本的 IOC 依赖查找实例 2. IOC 的两种实现方式 2.1 依赖查找(Dependency Lookup) 2.2 依赖注入(Depe ...

  8. java-12:spring MVC - 控制反转IOC,依赖注入DI

    学习spring框架之前,先理解几个概念: 1.第一部分:依赖倒置原则 2.第二部分:控制反转,控制反转容器(实例) 3.第三部分:控制反转,控制反转容器(全面理解,面试题) 综合性理解:控制反转(I ...

  9. springboot的IOC依赖注入与控制反转-举例(转载+自己整理)

    这个记录是对[1]的整理和简化,便于日后遗忘时迅速恢复记忆. Spring框架有四大原则: ①使用POJO进行轻量级与最小侵入式开发 ②通过依赖注入和基于接口编程实现松耦合 ③通过AOP和默认习惯进行 ...

最新文章

  1. Rust 1.30带来更多元编程支持,并改进了模块系统
  2. bzoj1601【Usaco2008 Oct】灌水
  3. key位置 win10生成的ssh_Git实现ssh免密登录
  4. gophp解释器_【干货】Gisp 解释器 Golang 辅助开发工具
  5. 【无标题】物联网安全挑战:为什么企业现在必须评估它们
  6. php带参数跳转页面,如何带参数跳转php界面_后端开发
  7. AndroidStudio安卓原生开发_activity中意图过滤器_Intentfilter之data数据---Android原生开发工作笔记95
  8. 计算机的录像功能在哪里找,电脑录像的功能在哪,快速开启的方法分享
  9. OpenCV3.1安装包下载
  10. 如何扒微信小程序源码
  11. 用C语言写Badapple
  12. 自学iOS开发的一些经验
  13. 云之家定位拍照怎么破解
  14. crc 校验错误_信道编码之循环冗余校验(CRC)
  15. unity android 30帧,解除某Unity游戏的30帧帧率限制
  16. 苹果微信多开_苹果手机微信双开,微信多开有哪些可以推荐的?
  17. windows操作系统死机代码及其含义解剖
  18. C# 开启HTTP监听服务与线程数量控制
  19. 计算机文化(20版)答案
  20. 价值文明的新纪元——解析Nirvana Chain钱包生态

热门文章

  1. 黑莓Torch 9800,了无新意落后主流机型
  2. Trade off between bias and variance
  3. dbvisualizer free 9.5.6中文乱码
  4. epson me 1+只有主机能打印不能共享网络打印问题的处理
  5. jQuery常用知识点总结以及平时封装常用函数
  6. linux上部署javaWeb项目
  7. 【51nod 1154】 回文串划分
  8. LeetCode - 7 - Reverse Integer
  9. 后端开发如何设计数据库系列文章(一)设计传统系统表结构(Java开发)
  10. linux python开发环境sql数据迁移到mysql_运用Python语言编写获取Linux基本系统信息(三):Python与数据库编程,把获取的信息存入数据库...