责任链和策略设计模式-基于Java编程语言

京东云开发者
• 阅读 569

作者:京东物流 钟磊

1 前言

最近在梳理接口逻辑的时候发现,代码中使用的策略和责任链设计模式给我留下了非常深刻的印象。一个业务逻辑流程通常非常适合使用责任链和策略设计模式来实现,因为一个业务需求通常可以拆分成一个个独立的逻辑处理单元并按顺序组合而成,而责任链设计模式可以很好的链接整个业务流程,同时策略设计模式可以将业务中变化的算法部分抽离出来,从而复用主要的公共逻辑并可以灵活替换业务算法,使用这两种设计模式可以灵活扩展我们的代码以适应不同的业务需求。由于这两种设计模式非常实用,下面简单介绍一下我对这两种设计模式的理解和它们在Spring框架源码中的应用。

2 责任链设计模式的一般定义

责任链设计模式是设计模式中的一种行为型设计模式。其基础结构类似于一个链条,整个链条由一个个单独的链环组成,每个链环在程序代码中就是一个独立的处理单元,每个处理单元都有自己负责的独特逻辑,当一个处理请求来到这个链条后,会依次沿着每个处理单元进行传递直到这个请求被处理完毕为止。

2.1 使用场景:

1.一个业务请求需要经过一组处理单元的处理。这种场景类似于一个业务逻辑流程中设置了多个功能不同的处理单元,一个业务请求需要经过这多个处理单元的串行处理。

责任链和策略设计模式-基于Java编程语言

2.程序中存在能处理同一个请求的多个处理单元,但决定具体使用哪个处理单元需要在程序运行时根据请求动态确定。

责任链和策略设计模式-基于Java编程语言

2.2 类图结构和Spring AOP框架中的应用

1:类图结构

责任链和策略设计模式-基于Java编程语言

2:责任链设计模式在Spring AOP框架中的应用

Spring框架中的AOP模块就使用了责任链设计模式将目标方法的一次调用过程包装成了一条方法调用链来增强目标方法。Spring AOP模块中包括了Before、After、AfterReturning、AfterThrowing、Around这五种通知方法,Spring AOP模块将这些通知方法和目标方法通过动态代理的方式包装成了一条调用链来分别履行各个通知模块的处理逻辑,下面是Spring AOP的源码分析图。

责任链和策略设计模式-基于Java编程语言

Spring AOP通过递归的方式来实现责任链的功能,首先将所有的通知方法进行排序,然后利用一个List索引来控制整个执行流程的开始和结束,在整个责任链中Before通知负责执行前置处理,After通知负责执行后置处理,AfterReturning方法负责在目标方法成功执行返回后执行处理逻辑,AfterThrowing方法负责在目标方法异常执行后执行处理逻辑。Spring AOP将通知方法包装成方法调用链上的每一个节点,巧妙地利用责任链模式完成了目标方法的处理增强。

3:泛化的责任链设计模式

在日常代码的编写中,责任链设计模式并不需要如此严格的结构,只要代码整体流程由一个个独立的处理单元构成,并且按一定顺序组合组合而成,那么也可以看作是一种更加泛化的责任链设计模式,也能很好的满足开闭原则,例如下面这种更加常用的代码结构。

责任链和策略设计模式-基于Java编程语言

上面这种结构同样也能实现责任链处理功能,也可以更加简洁的进行编写,同样可以很好的进修改和灵活扩展,在维护代码的适合也会更加清晰。

2.3 责任链模式的优点:

1.每个处理节点都有自己的独特的处理逻辑,明确各自在整个流程中的职责,符合类的单一职责原则。

2.构建的责任链可以根据业务需求进行灵活改变,能动态进行顺序调整以及动态插拔,满足重要的开闭原则。

3.降低了请求发送者以及请求处理者之间的耦合度。

3 策略设计模式的一般定义

策略设计模式同样也是设计模式中的一种行为型设计模式,其在结构上的表现就是将可变的算法策略部分从业务代码逻辑中独立出来,将这些算法策略形成策略池,从而可以随时替换和更新,使得我们的代码结构更加灵活、更易扩展。

3.1 使用场景

1:当代码需要根据上下文逻辑来选择使用不同的业务算法时,我们可以使用策略设计模式来优化代码的判断结构,从而避免大量的if/else分支判断。

责任链和策略设计模式-基于Java编程语言

2:当代码的主体处理逻辑大致相同,仅仅在部分的业务算法上存在不同时,可以将这些不同的业务算法抽离出来,从而能避免大量重复的代码编写,并能复用主体代码逻辑。

责任链和策略设计模式-基于Java编程语言

3.2 类图结构和Spring框架中的应用

1:类图结构

责任链和策略设计模式-基于Java编程语言

2:Spring框架中的应用

Spring框架中给我们开发者留下了非常多的扩展策略点,实现了可动态插拔的功能扩展,其中典型的一个策略扩展点就是BeanPostProcessor接口,BeanPostProcessor接口允许我们在Bean的初始化前和初始化后做一些逻辑处理策略来改变Bean的属性,允许我们对Bean进行改造和个性化,Spring AOP就是利用BeanPostProcessor这个策略扩展点实现了动态代理Bean的创建,下面是Spring AOP后置处理器的源码分析。

责任链和策略设计模式-基于Java编程语言

Spring AOP通过导入AspectJAwareAdvisorAutoProxyCreator这个实现了BeanPostProcessor接口的后置处理器,在Bean初始化后进行了一个动态代理类的创建,其在postProcessAfterInitaliztion方法中的WarpIfNecessary中方法中实现了代理类的创建。Spring框架利用这种模板加策略的设计模式让我们可以个性化扩展框架的功能,让框架变得非常灵活可扩展,我们也可以根据业务需求加入自己的BeanPostProcessor策略来实现自己的独特逻辑,很好地满足了重要的开闭设计原则。

3:泛化的策略设计模式

同样地我们也不必严格按照定义的策略设计模式进行编写,只要在整体上满足业务主体逻辑不变,将变化部分抽离出来,形成可以按需扩展的策略思想就行了,下面以SpringBoot自动装配的源码来说明这种更加泛化的策略模式。SpringBoot自动装配机制是按照用户当前的代码运行环境并结合@Conditional注解来动态为我们自动加载需要使用的类,这种策略设计模式是一种更加泛化的策略设计模式,同样满足策略设计模式的按需选择,动态插拔的设计原则。SpringBoot的自动装配机制的原理如下:

SpringBoot在@EnableAutoConfiguration注解中使用了@import注解导入了
EnableAutoConfigurationImportSelector类。

责任链和策略设计模式-基于Java编程语言

利用EnableAutoConfigurationImportSelector类的selectImports方法来加载jar包里面META-INF/spring.factories文件中配好的类。

责任链和策略设计模式-基于Java编程语言

责任链和策略设计模式-基于Java编程语言

最后结合各种@Conditional注解来实现按需加载的策略设计模式。

责任链和策略设计模式-基于Java编程语言

SpringBoot这种按需自动装配的策略设计思想,在结构上并不严格符合策略设计模式的结构,但他的整体设计思想非常符合策略设计模式,我们在项目中也可以通过配置文件的方式来灵活切换我们代码中使用的策略算法。

3.3 策略设计模式的优点:

1:可以在程序运行时动态选择切换需要使用的独立业务算法。

2:将可变的业务算法与业务主体逻辑剥离,实现更加灵活的维护和扩展。

3:满足开闭原则,无需修改原有的代码逻辑就能实现不同业务算法灵活切换。

4 结合策略设计模式和责任链设计模式

将策略设计模式和责任链设计模式进行结合就能形成灵活可扩展的流程结构,能应对多变的业务需求,下面是将两者进行结合的结构图。

责任链和策略设计模式-基于Java编程语言

一个request在经过每一个handler处理单元时,会根据request的上下文内容选择合适的策略进行处理,然后将这所有的handler串联起来形成完整的业务流程。

5 总结

在日常代码的编写中,业务需求的变化总是不定的,这样会导致我们的代码会频繁的随着需求的改变进行调整,稍加不注意的话就会导致我们的代码非常臃肿和复杂,累加到一定程度后会变得难以维护,这时预先使用合适的代码设计模式能有效的缓解这种情况,文中描述的责任链和策略设计模式能有效满足代码编写的开闭原则,能更加有效的应对随时变化的业务需求。

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Wesley13 Wesley13
2年前
PPDB:今晚老齐直播
【今晚老齐直播】今晚(本周三晚)20:0021:00小白开始“用”飞桨(https://www.oschina.net/action/visit/ad?id1185)由PPDE(飞桨(https://www.oschina.net/action/visit/ad?id1185)开发者专家计划)成员老齐,为深度学习小白指点迷津。
Wesley13 Wesley13
2年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
2年前
PHP创建多级树型结构
<!lang:php<?php$areaarray(array('id'1,'pid'0,'name''中国'),array('id'5,'pid'0,'name''美国'),array('id'2,'pid'1,'name''吉林'),array('id'4,'pid'2,'n
Wesley13 Wesley13
2年前
Java日期时间API系列36
  十二时辰,古代劳动人民把一昼夜划分成十二个时段,每一个时段叫一个时辰。二十四小时和十二时辰对照表:时辰时间24时制子时深夜11:00凌晨01:0023:0001:00丑时上午01:00上午03:0001:0003:00寅时上午03:00上午0
Wesley13 Wesley13
2年前
00_设计模式之语言选择
设计模式之语言选择设计模式简介背景设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。设计模式(Designpattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的
Stella981 Stella981
2年前
Jenkins 插件开发之旅:两天内从 idea 到发布(上篇)
本文首发于:Jenkins中文社区(https://www.oschina.net/action/GoToLink?urlhttp%3A%2F%2Fjenkinszh.cn)!huashan(https://oscimg.oschina.net/oscnet/f499d5b4f76f20cf0bce2a00af236d10265.jpg)
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_