点击上方“芋道源码”,选取“设为星标”
管她前浪,还是后浪?
能浪的浪,才是好浪!
每日 10:33 更新文案,每日掉亿点点头发...
源码精品专栏
原创 | Java 2021 超神之路,很肝~
中文仔细注释的开源项目
RPC 框架 Dubbo 源码解析
网络应用框架 Netty 源码解析
信息中间件 RocketMQ 源码解析
数据库中间件 Sharding-JDBC 和 MyCAT 源码解析
作业调度中间件 Elastic-Job 源码解析
分布式事务中间件 TCC-Transaction 源码解析
Eureka 和 Hystrix 源码解析
Java 并发源码
源自:网络 什么是责任链场景反例初步改造缺点责任链改造责任链工厂改造聊聊其他
近期,我让团队内一位成员写了一个导入功能。他运用了责任链模式,代码堆的非常多,bug 亦多,无达到我预期的效果。实质上,针对导入功能,我认为模版办法更合适!为此,隔壁团队亦拿出咱们的案例,进行了集体 code review。
学好设计模式,且不要为了练习,强行运用!让本来100行就能实现的功能,写了 3000 行!
对错暂且不论,咱们先一块瞧瞧责任链设计模式吧!
「责任链模式」 是一种行径设计模式, 准许你将请求沿着处理者链进行发送。收到请求后, 每一个处理者均可对请求进行处理, 或将其传递给链上的下个处理者。
基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能。
项目位置:https://github.com/YunaiV/ruoyi-vue-pro
责任链的运用场景还是比较多的 多要求流程判断:权限掌控ERP 系统流程审批:总经理、人事经理、项目经理Java 过滤器 的底层实现 Filter倘若不运用该设计模式,那样当需求有所改变时,就会使得代码臃肿或难以守护,例如下面的例子
假设此刻有一个闯关游戏,进入下一关的要求是上一关的分数要高于xx 游戏一共 3 个关卡进入第二关必须第1关的游戏得分大于等于 80进入第三关必须第二关的游戏得分大于等于 90那样代码能够这般写 //第1关public class FirstPassHandler
{ public int handler()
{ System.out.println("第1关-->FirstPassHandler"
); return 80
;
}
} //第二关public class SecondPassHandler
{ public int handler()
{ System.out.println("第二关-->SecondPassHandler"
); return 90
;
}
} //第三关public class ThirdPassHandler
{ public int handler()
{ System.out.println("第三关-->ThirdPassHandler,这是最后一关啦"
); return 95
;
}
} //客户端public class HandlerClient
{ public static void main(String[] args)
{ FirstPassHandler firstPassHandler =new FirstPassHandler();//第1关 SecondPassHandler secondPassHandler = new SecondPassHandler();//第二关 ThirdPassHandler thirdPassHandler = newThirdPassHandler();//第三关 int
firstScore = firstPassHandler.handler(); //第1关的分数大于等于80则进入第二关 if(firstScore >= 80
){ int
secondScore = secondPassHandler.handler(); //第二关的分数大于等于90则进入第二关 if(secondScore >= 90
){
thirdPassHandler.handler();
}
}
}
}
那样倘若这个游戏有100关,咱们的代码很可能就会写成这个样子 if(第1
关经过){ // 第2关 游戏 if(第2
关经过){ // 第3关 游戏 if(第3
关经过){ // 第4关 游戏 if(第4
关经过){ // 第5关 游戏 if(第5
关经过){ // 第6关 游戏 if(第6
关经过){ //...
}
}
}
}
}
}
这种代码不仅冗余,并且当咱们要将某两关进行调节时会对代码非常大的改动,这种操作的危害是很高的,因此呢,该写法非常糟糕
怎样处理这个问题,咱们能够经过链表将每一关连接起来,形成责任链的方式,第1关经过后是第二关,第二关经过后是第三关 ....,这般客户端就不必须进行多重 if 的判断了 public class FirstPassHandler
{
/**
* 第1关的下一关是 第二关 */
private
SecondPassHandler secondPassHandler; public void setSecondPassHandler(SecondPassHandler secondPassHandler)
{ this
.secondPassHandler = secondPassHandler;
} //本关卡游戏得分 private int play()
{ return 80
;
} public int handler()
{ System.out.println("第1关-->FirstPassHandler"
); if(play() >= 80
){ //分数>=80 并且存在下一关才进入下一关 if(this.secondPassHandler != null
){ return this
.secondPassHandler.handler();
}
} return 80
;
}
} public class SecondPassHandler
{
/**
* 第二关的下一关是 第三关 */
private
ThirdPassHandler thirdPassHandler; public void setThirdPassHandler(ThirdPassHandler thirdPassHandler)
{ this
.thirdPassHandler = thirdPassHandler;
} //本关卡游戏得分 private int play()
{ return 90
;
} public int handler()
{ System.out.println("第二关-->SecondPassHandler"
); if(play() >= 90
){ //分数>=90 并且存在下一关才进入下一关 if(this.thirdPassHandler != null
){ return this
.thirdPassHandler.handler();
}
} return 90
;
}
} public class ThirdPassHandler
{ //本关卡游戏得分 private int play()
{ return 95
;
}
/**
* 这是最后一关,因此呢无下一关 */
public int handler()
{ System.out.println("第三关-->ThirdPassHandler,这是最后一关啦"
); return
play();
}
} public class HandlerClient
{ public static void main(String[] args)
{ FirstPassHandler firstPassHandler = new FirstPassHandler();//第一关 SecondPassHandler secondPassHandler = new SecondPassHandler();//第二关 ThirdPassHandler thirdPassHandler = new ThirdPassHandler();//第三关firstPassHandler.setSecondPassHandler(secondPassHandler);//第1关的下一关是第二关 secondPassHandler.setThirdPassHandler(thirdPassHandler);//第二关的下一关是第三关 //说明:由于第三关是最后一关,因此呢无下一关 //起始调用第1关 每一个关卡是不是进入下一关卡 在每一个关卡中判断
firstPassHandler.handler();
}
}
现有模式的缺点 每一个关卡中都有下一关的成员变量并且是不同样的,形成链很不方便代码的扩展性非常欠好
既然每一个关卡中都有下一关的成员变量并且是不同样的,那样咱们能够在关卡上抽象出一个父类或接口,而后每一个详细的关卡去继承或实现有了思路,咱们先来简单介绍一下责任链设计模式的「基本构成」 抽象处理者(Handler)角色:定义一个处理请求的接口,包括抽象处理办法和一个后继连接。详细处理者(Concrete Handler)角色:实现抽象处理者的处理办法,判断能否处理这次请求,倘若能够处理请求则处理,否则将该请求转给它的后继者。客户类(Client)角色:创建处理链,并向链头的详细处理者对象提交请求,它不关心处理细节和请求的传递过程。责任链改造public abstract class AbstractHandler
{
/**
* 下一关用当前抽象类来接收 */
protected
AbstractHandler next; public void setNext(AbstractHandler next)
{ this
.next = next;
} public abstract int handler()
;
} public class FirstPassHandler extends AbstractHandler
{ private int play()
{ return 80
;
} @Override public int handler()
{ System.out.println("第1关-->FirstPassHandler"
); int
score = play(); if(score >= 80
){ //分数>=80 并且存在下一关才进入下一关 if(this.next != null
){ return this
.next.handler();
}
} return
score;
}
} public class SecondPassHandler extends AbstractHandler
{ private int play()
{ return 90
;
} public int handler()
{ System.out.println("第二关-->SecondPassHandler"
); int
score = play(); if(score >= 90
){ //分数>=90 并且存在下一关才进入下一关 if(this.next != null
){ return this
.next.handler();
}
} return
score;
}
} public class ThirdPassHandler extends AbstractHandler
{ private int play()
{ return 95
;
} public int handler()
{ System.out.println("第三关-->ThirdPassHandler"
); int
score = play(); if(score >= 95
){ //分数>=95 并且存在下一关才进入下一关 if(this.next != null
){ return this
.next.handler();
}
} return
score;
}
} public class HandlerClient
{ public static void main(String[] args)
{ FirstPassHandler firstPassHandler = new FirstPassHandler();//第1关 SecondPassHandler secondPassHandler = newSecondPassHandler();//第二关 ThirdPassHandler thirdPassHandler = new ThirdPassHandler();//第三关 // 和上面无更改的客户端代码相比,仅有这儿的set办法出现变化,其他都是同样的firstPassHandler.setNext(secondPassHandler);//第1关的下一关是第二关 secondPassHandler.setNext(thirdPassHandler);//第二关的下一关是第三关 //说明:由于第三关是最后一关,因此呢无下一关 //从第1个关卡起始
firstPassHandler.handler();
}
}
针对上面的请求链,咱们亦能够把这个关系守护到配置文件中或一个枚举中。我将运用枚举来教会大众怎么动态的配置请求链并且将每一个请求者形成一条调用链。 public enum
GatewayEnum { // handlerId, 拦截者名叫作,全限定类名,preHandlerId,nextHandlerId API_HANDLER(new GatewayEntity(1, "api接口限流", "cn.dgut.design.chain_of_responsibility.GateWay.impl.ApiLimitGatewayHandler", null, 2
)), BLACKLIST_HANDLER(new GatewayEntity(2, "黑名单拦截", "cn.dgut.design.chain_of_responsibility.GateWay.impl.BlacklistGatewayHandler", 1, 3
)), SESSION_HANDLER(new GatewayEntity(3, "用户会话拦截", "cn.dgut.design.chain_of_responsibility.GateWay.impl.SessionGatewayHandler", 2, null
)),
;
GatewayEntity gatewayEntity; public GatewayEntity getGatewayEntity()
{ return
gatewayEntity;
}
GatewayEnum(GatewayEntity gatewayEntity) { this
.gatewayEntity = gatewayEntity;
}
} public class GatewayEntity
{ private
String name; private
String conference; private
Integer handlerId; private
Integer preHandlerId; private
Integer nextHandlerId;
} public interface GatewayDao
{
/**
* 按照 handlerId 获取配置项 * @param
handlerId * @return */
GatewayEntity getGatewayEntity(Integer handlerId)
;
/**
* 获取第1个处理者 * @return */
GatewayEntity getFirstGatewayEntity()
;
} public class GatewayImpl implements GatewayDao
{
/**
* 初始化,将枚举中配置的handler初始化到map中,方便获取 */
private static Map<Integer, GatewayEntity> gatewayEntityMap = new
HashMap<>(); static
{
GatewayEnum[] values = GatewayEnum.values(); for
(GatewayEnum value : values) {
GatewayEntity gatewayEntity = value.getGatewayEntity();
gatewayEntityMap.put(gatewayEntity.getHandlerId(), gatewayEntity);
}
} @Override public GatewayEntity getGatewayEntity(Integer handlerId)
{ return
gatewayEntityMap.get(handlerId);
} @Override public GatewayEntity getFirstGatewayEntity()
{ for
(Map.Entry entry : gatewayEntityMap.entrySet()) {
GatewayEntity value = entry.getValue(); // 无上一个handler的便是第1个 if (value.getPreHandlerId() == null
) { return
value;
}
} return null
;
}
} public class GatewayHandlerEnumFactory
{ private staticGatewayDao gatewayDao =new
GatewayImpl(); // 供给静态办法,获取第1个handler public static GatewayHandler getFirstGatewayHandler()
{
GatewayEntity firstGatewayEntity = gatewayDao.getFirstGatewayEntity();
GatewayHandler firstGatewayHandler = newGatewayHandler(firstGatewayEntity); if (firstGatewayHandler == null
) { return null
;
}
GatewayEntity tempGatewayEntity = firstGatewayEntity; Integer nextHandlerId = null
;
GatewayHandler tempGatewayHandler = firstGatewayHandler; // 迭代遍历所有handler,以及将它们链接起来 while ((nextHandlerId = tempGatewayEntity.getNextHandlerId()) != null
) {
GatewayEntity gatewayEntity = gatewayDao.getGatewayEntity(nextHandlerId);
GatewayHandler gatewayHandler = newGatewayHandler(gatewayEntity);
tempGatewayHandler.setNext(gatewayHandler);
tempGatewayHandler = gatewayHandler;
tempGatewayEntity = gatewayEntity;
} // 返回第1个handler return
firstGatewayHandler;
}
/**
* 反射实体化详细的处理者 * @param
firstGatewayEntity * @return */
private static GatewayHandler newGatewayHandler(GatewayEntity firstGatewayEntity)
{ // 获取全限定类名
String className = firstGatewayEntity.getConference(); try
{ // 按照全限定类名,加载并初始化该类,即会初始化该类的静态段
Class clazz = Class.forName(className);
return
(GatewayHandler) clazz.newInstance(); } catch
(ClassNotFoundException | IllegalAccessException | InstantiationException e) {
e.printStackTrace();
} return null
;
}
} public class GetewayClient
{ public static void main(String[] args)
{
GetewayHandler firstGetewayHandler = GetewayHandlerEnumFactory.getFirstGetewayHandler();
firstGetewayHandler.service();
}
}
基于微服务的思想,构建在 B2C 电商场景下的项目实战。核心技术栈,是 Spring Boot + Dubbo 。将来,会重形成 Spring Cloud Alibaba 。
项目位置:https://github.com/YunaiV/onemall
设计模式有非常多,责任链只是其中的一种,我觉得特别有意思,非常值得一学。
设计模式确实是一门艺术,仍需奋斗呀!
欢迎加入我的知识星球,一块探讨架构,交流源码。加入方式,长按下方二维码噢
:
已在知识星球更新源码解析如下:
近期更新《芋道 SpringBoot 2.X 入门》系列,已然 101 余篇,覆盖了 MyBatis、Redis、MongoDB、ES、分库分表、读写分离、SpringMVC、Webflux、权限、WebSocket、Dubbo、RabbitMQ、RocketMQ、Kafka、性能测试等等内容。
供给近 3W 行代码的 SpringBoot 示例,以及超 4W 行代码的电商微服务项目。
获取方式:点“在看”,关注公众号并回复 666 领取,更加多内容持续奉上。
文案有帮忙的话,在看,转发吧。
谢谢支持哟 (*^__^*)
|