巧用拦截器:高效的扩展点设计

简介:

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

 

扩展点使用场景


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

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

 

扩展点设计方案选型


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

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

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

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

 

实现


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

Interceptor

 

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

image

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

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

image

 

总结


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

目录
相关文章
|
3月前
|
NoSQL 算法 Java
后端接口性能优化分析-程序结构优化(中)
后端接口性能优化分析-程序结构优化
38 0
|
6月前
|
存储 缓存 监控
《优化接口设计的思路》系列:第二篇—接口用户上下文的设计与实现
大家好!我是sum墨,一个一线的底层码农,平时喜欢研究和思考一些技术相关的问题并整理成文,限于本人水平,如果文章和代码有表述不当之处,还请不吝赐教。 作为一名从业已达六年的老码农,我的工作主要是开发后端Java业务系统,包括各种管理后台和小程序等。在这些项目中,我设计过单/多租户体系系统,对接过许多开放平台,也搞过消息中心这类较为复杂的应用,但幸运的是,我至今还没有遇到过线上系统由于代码崩溃导致资损的情况。这其中的原因有三点:一是业务系统本身并不复杂;二是我一直遵循某大厂代码规约,在开发过程中尽可能按规约编写代码;三是经过多年的开发经验积累,我成为了一名熟练工,掌握了一些实用的技巧。
52 0
|
3月前
|
NoSQL Java Redis
后端接口性能优化分析-程序结构优化(上)
后端接口性能优化分析-程序结构优化
165 0
|
1月前
|
存储 Java 测试技术
Cookie复用的妙用:数据处理中的高效利器!
本文介绍了Cookie在Web自动化登录中的应用。Cookie是存储在浏览器上的认证数据,用于身份验证和记录登录信息。通过获取和管理Cookie,自动化测试时可模拟用户登录状态,提高测试效率。使用Cookie自动化登录的步骤包括:登录获取Cookie、存储Cookie、读取Cookie并植入浏览器。Python和Java示例代码展示了如何实现这一过程。常见问题提醒注意Cookie的有效性和互踢机制,确保自动化测试的顺利进行。
37 0
|
3月前
|
缓存 Java 应用服务中间件
后端接口性能优化分析-程序结构优化(下)
后端接口性能优化分析-程序结构优化
50 0
|
4月前
|
设计模式
二十三种设计模式全面解析-装饰器模式的高级应用:打造灵活可扩展的通知系统
二十三种设计模式全面解析-装饰器模式的高级应用:打造灵活可扩展的通知系统
|
6月前
|
小程序 前端开发 API
阿里云小程序框架优点
阿里云小程序框架优点
|
6月前
|
监控 小程序 Java
《优化接口设计的思路》系列:第五篇—接口发生异常如何统一处理
大家好!我是sum墨,一个一线的底层码农,平时喜欢研究和思考一些技术相关的问题并整理成文,限于本人水平,如果文章和代码有表述不当之处,还请不吝赐教。
207 0
《优化接口设计的思路》系列:第五篇—接口发生异常如何统一处理
|
7月前
|
SQL 关系型数据库 MySQL
前后端交互接口性能速度优化
前后端交互接口性能速度优化
56 0
|
8月前
|
存储 算法 Java
探索Java数组:基础、特性与灵活应用
在Java编程中,数组是一种基础而重要的数据结构,它能够以紧凑的方式存储多个元素。无论是在简单的数据存储还是复杂的算法实现中,数组都扮演着不可或缺的角色。本文将引导您深入了解Java数组,包括数组的基本概念、特性、用法以及常见应用场景。