最近在设计框架时,需要设计一类扩展点,发现不能简单地继承或使用事件来给使用者提供 API。最终使用拦截器模式解决了 API 的设计。

扩展点使用场景

该扩展点的使用场景如下:

  1. 不能使用继承;需要在类型的继承体系外(非被扩展类型的子类)对类型进行扩展。
  2. 需要能在基本逻辑的执行前、后扩展新的逻辑,甚至可以使用新的逻辑替换基础逻辑。
  3. 对于性能敏感。由于该基础逻辑是比较核心的代码,需要尽量地减少扩展点带来的额外性能消耗、并尽量少地产生额外的对象。

扩展点设计方案选型


在框架设计时,扩展点设计主要通过几种类型的 API 来提供:虚方法、事件、接口。(关于扩展点的设计,详细的内容,参见:《Framework Design Guidelines 2nd Edition》第六章,扩展性设计。)而最常用、最方便使用者使用的扩展点则是前两个:虚方法和事件。

前两种扩展点设计方案的主要区别在于:

  1. 场景:继承中的虚方法主要是为类型的子类型进行扩展而提供的,而事件则主要是为继承体系外的类型来扩展继承体系内的类型的行为而提供的;
  2. 控制度:子类对虚方法进行重写时,可以在基类的基本方法前、后编写自己的扩展代码,同时还可以控制是否需要调用基类的方法;而事件要实现这些功能,需要提供逻辑前事件(Invoking)、逻辑后事件(Invoked),并通过类似 CancelEventArgs.IsCancel 属性等方式来控制是否需要执行基本的逻辑。
  3. 性能:虚方法的调用是非常高效的,也不会产生额外的对象。而事件的机制本质是委托列表,会遍历该列表进行调用,同时需要产生额外的委托对象;其次,由于 .NET 事件的设计中往往要求提供一个从 EventArgs 类型上继承的事件参数对象,在每次调用都构造并传递该对象,这也会产品额外的对象压力。

可以看出,如果是想设计一类提供给继承体系外类型进行扩展的扩展点, 虚方法、事件两类 API 都不合适。那我们只能在第三种方式上想办法:接口。接口的设计则非常灵活,而其实我上面描述的场景会经常遇到,所以应该提取出一类设计模式。经过一番思考,我发现其实拦截器模式比较适合该场景。拦截器模式本身注重对消息、方法的拦截处理,是一种继承体系外的扩展方法,并被大量用于 AOP 的实现。在这里采用该模式,我们更加关注其在真正核心方法调用前后的扩展机制、以及核心方法的阻断机制,以及最终扩展 API 提供的形式。

实现


该模式放到 Rafy 中实现提交时的扩展点后,类图如下:

扩展点使用方法也较简单,使用者继承拦截器,编写相应的扩展逻辑即可:

有一个细节需要注意:上图中能看到,方法的第一个参数也是一个自定义的参数类型 SubmitArgs。但是由于拦截器是一种链式调用,所以这个类型可以采用值类型;在此方法被大量调用时,相对于事件的扩展机制,没有大量的冗余对象。

在启动时,加入以下代码就可以把该拦截器添加到保存的拦截器列表中:

总结


拦截器模式实现起来比较简单,该模式的结构非常类似于 GOF 中的职责链模式,只是关注点不同。在使用它作为扩展点时,对于使用者来说也比较易用,而且性能相对于事件机制来说更好,所以可以直接作为一种常用的扩展点设计方案。

巧用拦截器:高效的扩展点设计相关推荐

  1. 【struts2】struts2拦截器

    struts2提供面向切面(AOP)编程的机制,拦截器便是一种成熟的AOP编程思想的实现,它提供一种机制使开发者能把相对独立的代码抽象出来,配置到action前后执行.拦截器interceptor类似 ...

  2. structs2拦截器详解

    Struts2(XWork)提供的拦截器的功能说明: 拦截器 名字 说明 Alias Interceptor alias 在不同请求之间将请求参数在不同名字件转换,请求内容不变 Chaining In ...

  3. springmvc java中转发_Springmvc中的转发重定向和拦截器的示例

    本文介绍了Springmvc中的转发重定向和拦截器的示例,分享给大家,具体如下: 可变参数在设计方法时,使用 数据类型...来声明参数类型,例如: public static void functio ...

  4. Springboot 自定义mybatis 拦截器,实现我们要的扩展

    前言 相信大家对拦截器并不陌生,对mybatis也不陌生. 有用过pagehelper的,那么对mybatis拦截器也不陌生了,按照使用的规则触发sql拦截,帮我们自动添加分页参数 . 那么今天,我们 ...

  5. 11.6 使用拦截器插件扩展代理功能

    博客已搬家, 更好阅读体验, 猛戳 http://www.jack-yin.com/coding/translation/activemq-in-action/1727.html 11.6 Exten ...

  6. SpringMVC (注解、拦截器、json、Ajax)

    SpringMVC 1.回顾MVC 1.1 什么是mvc MVC是模型(Model).视图(View).控制器(Controller)的简写,是一种软件设计规范. 是将业务逻辑.数据.显示分离的方法来 ...

  7. javaweb(http、servlet、maven、过滤器、拦截器)

    JavaWeb 标注:笔记大部分来自小狂神笔记 Http 什么是HTTP HTTP是基于客户端/服务端(C/S)的架构模型,通过一个可靠的链接来交换信息,是一个无状态的请求/响应协议. 一个HTTP& ...

  8. list mybatis 接收 类型_基于mybatis拦截器实现的一款简易影子表自动切换插件

    近期因工作需要,小编基于mybatis拦截器开发了一款简易影子表自动切换插件,可以根据配置实现动态修改表名,即将对原source table表的操作自动切换到对target table表的操作.该插件 ...

  9. druid拦截器_CMS基于SpringBoot+Shiro+Mybatis+Druid+layui后台管理系统

    contentManagerSystem后台管理系统 简介 contentManagerSystem,后台管理系统,采用SpringBoot构建整个项目框架,apacheShiro权限验证,mybat ...

最新文章

  1. quartus 修改 时钟_FPGAQuartusII时钟约束.doc
  2. tms570 can 接收大量数据_CAN通讯系列--AUTOSAR架构的CAN Interface7
  3. 编译x86架构的openwrt系统,让笔记本从u盘启动openwrt
  4. Error from server (Forbidden): Forbidden (user=system:anonymous, verb=get, resource=nodes, subresour
  5. mac抹掉磁盘重装系统未能与服务器取得联系_Mac重装系统不再难:苹果电脑重装系统教程...
  6. windows命令行启动常用工具
  7. SpringBoot | 第三十五章:Mybatis的集成和使用
  8. 关于机器人方面的sci论文_如何给论文润色?从这两个方面入手
  9. 使用sersync实现多台服务器实时同步文件
  10. windows优化大师怎么用_软件不能用又卸载不掉怎么办——用Windows自家的卸载工具吧(dos级卸载)...
  11. 3项目估算表_浮动油封生产项目可行性研究报告
  12. k8s踩坑记第2篇--3个IP折磨人的故事
  13. 数据库系统概论第五版(笔记+习题答案)(全)
  14. 一个40岁程序员的经历
  15. js 禁用输入法(伪) / keydown返回false仍然可以输入的问题
  16. 身高体重排序-华为OD
  17. 石墨烯具有非常良好的光学特性,可能拥有一个非线性相移的光学非线性克尔效应-供应石墨烯/生态炭复合材料 聚苯乙烯/氧化石墨烯(PS/GO)复合微球 石墨烯-四氧化三铁(Fe_3O_4)微球复合材料
  18. 吃海鲜搭配什么菜好 搭配这些健康又美味
  19. ubuntu上传文件到百度网盘
  20. 俺的作品也开始有盗版了... hiahia... 奇怪, 为什么我反而很得意涅...

热门文章

  1. mysql select查询2个表_mysql – 为SELECT查询合并2个表?
  2. ubuntu开启root登陆
  3. 6升小米6——算法解题
  4. C#中读取带有Xmlns命名空间的XML文件
  5. Android的BUG(四) - Android app的卡死问题
  6. 象棋中常用的最小值最大值算法及剪枝算法
  7. MySQL及其分支或衍生版
  8. ApplicationContextAware
  9. wp7 通过后台代码给ListBoxItem添加ContextMenu 属性
  10. 转载 cFos vs cFosSpeed