位置: IT常识 - 正文

同事写了一个责任链模式,bug 无数...

编辑:rootadmin
背景 最近,我让团队内一位成员写了一个导入功能。他使用了责任链模式,代码堆的非常多,bug 也多,没有达到我预期的效果。 实际上,针对导入功能,我认为模版方法更合适!为此,隔壁团队也拿出我们的案例,进行了集体 code review。 学好设计模式,且不要为了练习,强行使用!让原本 100 行就能实 ... 背景

推荐整理分享同事写了一个责任链模式,bug 无数...,希望有所帮助,仅作参考,欢迎阅读内容。

文章相关热门搜索词:,内容如对您有帮助,希望把文章链接给更多的朋友!

最近,我让团队内一位成员写了一个导入功能。他使用了责任链模式,代码堆的非常多,bug 也多,没有达到我预期的效果。

实际上,针对导入功能,我认为模版方法更合适!为此,隔壁团队也拿出我们的案例,进行了集体 code review。

学好设计模式,且不要为了练习,强行使用!让原本 100 行就能实现的功能,写了 3000 行!对错暂且不论,我们先一起看看责任链设计模式吧!

什么是责任链

责任链模式是一种行为设计模式, 允许你将请求沿着处理者链进行发送。收到请求后, 每个处理者均可对请求进行处理, 或将其传递给链上的下个处理者。

使用场景

责任链的使用场景还是比较多的:

多条件流程判断:权限控制ERP 系统流程审批:总经理、人事经理、项目经理Java 过滤器的底层实现 Filter

如果不使用该设计模式,那么当需求有所改变时,就会使得代码臃肿或者难以维护,例如下面的例子。

| 反例

假设现在有一个闯关游戏,进入下一关的条件是上一关的分数要高于 xx:

游戏一共 3 个关卡进入第二关需要第一关的游戏得分大于等于 80进入第三关需要第二关的游戏得分大于等于 90

那么代码可以这样写:

//第一关public class FirstPassHandler { public int handler(){ System.out.println("第一关-->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();//第一关 SecondPassHandler secondPassHandler = new SecondPassHandler();//第二关 ThirdPassHandler thirdPassHandler = new ThirdPassHandler();//第三关 int firstScore = firstPassHandler.handler(); //第一关的分数大于等于80则进入第二关 if(firstScore >= 80){ int secondScore = secondPassHandler.handler(); //第二关的分数大于等于90则进入第二关 if(secondScore >= 90){ thirdPassHandler.handler(); } } }}同事写了一个责任链模式,bug 无数...

那么如果这个游戏有 100 关,我们的代码很可能就会写成这个样子:

if(第1关通过){ // 第2关 游戏 if(第2关通过){ // 第3关 游戏 if(第3关通过){ // 第4关 游戏 if(第4关通过){ // 第5关 游戏 if(第5关通过){ // 第6关 游戏 if(第6关通过){ //... } } } } }}

这种代码不仅冗余,并且当我们要将某两关进行调整时会对代码非常大的改动,这种操作的风险是很高的,因此,该写法非常糟糕。

更多设计模式系列教程:https://www.javastack.cn/

| 初步改造

如何解决这个问题,我们可以通过链表将每一关连接起来,形成责任链的方式,第一关通过后是第二关,第二关通过后是第三关....

这样客户端就不需要进行多重 if 的判断了:

public class FirstPassHandler { /** * 第一关的下一关是 第二关 */ private SecondPassHandler secondPassHandler; public void setSecondPassHandler(SecondPassHandler secondPassHandler) { this.secondPassHandler = secondPassHandler; } //本关卡游戏得分 private int play(){ return 80; } public int handler(){ System.out.println("第一关-->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);//第一关的下一关是第二关 secondPassHandler.setThirdPassHandler(thirdPassHandler);//第二关的下一关是第三关 //说明:因为第三关是最后一关,因此没有下一关 //开始调用第一关 每一个关卡是否进入下一关卡 在每个关卡中判断 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("第一关-->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();//第一关 SecondPassHandler secondPassHandler = new SecondPassHandler();//第二关 ThirdPassHandler thirdPassHandler = new ThirdPassHandler();//第三关 // 和上面没有更改的客户端代码相比,只有这里的set方法发生变化,其他都是一样的 firstPassHandler.setNext(secondPassHandler);//第一关的下一关是第二关 secondPassHandler.setNext(thirdPassHandler);//第二关的下一关是第三关 //说明:因为第三关是最后一关,因此没有下一关 //从第一个关卡开始 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); /** * 获取第一个处理者 * @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<Integer, GatewayEntity> entry : gatewayEntityMap.entrySet()) { GatewayEntity value = entry.getValue(); // 没有上一个handler的就是第一个 if (value.getPreHandlerId() == null) { return value; } } return null; }}public class GatewayHandlerEnumFactory { private static GatewayDao gatewayDao = new GatewayImpl(); // 提供静态方法,获取第一个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; } // 返回第一个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(); }}

设计模式有很多,责任链只是其中的一种,我觉得很有意思,非常值得一学。设计模式确实是一门艺术,仍需努力呀!

版权

本文链接地址:https://www.jiuchutong.com/zhishi/304792.html 转载请保留说明!

上一篇:一起爬山吗?《隐秘的角落》为什么译成Cat's Cradle?(你要和我一起爬山吗)

下一篇:php使用array_diff去除元素(php使用while循环计算1到100的和)

  • 抖音里面我的收藏怎么找不到了(抖音里面我的收入怎么才会有收入)

    抖音里面我的收藏怎么找不到了(抖音里面我的收入怎么才会有收入)

  • 哈罗单车怎么取消月卡自动续费(哈罗单车怎么取消自动续费功能)

    哈罗单车怎么取消月卡自动续费(哈罗单车怎么取消自动续费功能)

  • 小米平板和手机怎么互联(小米平板和手机互联)

    小米平板和手机怎么互联(小米平板和手机互联)

  • 苹果手机屏幕变黄如何恢复(苹果手机屏幕变成x光片是怎么回事)

    苹果手机屏幕变黄如何恢复(苹果手机屏幕变成x光片是怎么回事)

  • 苹果x屏幕摔出竖白条(苹果x屏幕摔出黑色块修多少钱)

    苹果x屏幕摔出竖白条(苹果x屏幕摔出黑色块修多少钱)

  • qq的字符什么意思(qq里的幸运字符是什么意思)

    qq的字符什么意思(qq里的幸运字符是什么意思)

  • 抖音赞全部作品会被限流吗(抖音赞全部作品怎么看)

    抖音赞全部作品会被限流吗(抖音赞全部作品怎么看)

  • 小米按键音怎么设置(小米按键音怎么换)

    小米按键音怎么设置(小米按键音怎么换)

  • 手机闪屏是什么意思(手机闪屏是什么问题怎么解决)

    手机闪屏是什么意思(手机闪屏是什么问题怎么解决)

  • 手机号多久不交费会被自动注销(手机号多久不交钱会注销吗)

    手机号多久不交费会被自动注销(手机号多久不交钱会注销吗)

  • 抖音显示关注太快休息一下怎么回事(抖音怎么刷关注)

    抖音显示关注太快休息一下怎么回事(抖音怎么刷关注)

  • 美拍可以拍多久的视频(美拍可以拍多长时间)

    美拍可以拍多久的视频(美拍可以拍多长时间)

  • 微信听不到视频通话的提示音怎么办(微信听不到视频聊天提示音)

    微信听不到视频通话的提示音怎么办(微信听不到视频聊天提示音)

  • 聊天记录漫游七天什么意思(聊天记录漫游7天)

    聊天记录漫游七天什么意思(聊天记录漫游7天)

  • vivo哪款手机有nfc功能(vivo手机在哪看型号)

    vivo哪款手机有nfc功能(vivo手机在哪看型号)

  • 存取最快的存储器是(存取速度最快的存储)

    存取最快的存储器是(存取速度最快的存储)

  • 快手设置特别关心有什么用(快手设置特别关注对方知道吗)

    快手设置特别关心有什么用(快手设置特别关注对方知道吗)

  • 无线耳机充电仓充电时耳机可以不在里面吗(无线耳机充电仓进水了怎么办)

    无线耳机充电仓充电时耳机可以不在里面吗(无线耳机充电仓进水了怎么办)

  • vivos5视频美颜怎么设置(vivo 视频美颜)

    vivos5视频美颜怎么设置(vivo 视频美颜)

  • b站怎么缓存音频(b站怎么缓存音乐文件)

    b站怎么缓存音频(b站怎么缓存音乐文件)

  • 微信号被别人注册了怎么办(微信号被别人注销了怎么恢复)

    微信号被别人注册了怎么办(微信号被别人注销了怎么恢复)

  • 小黄车怎么取消自动续费(小黄车怎么取消实名认证)

    小黄车怎么取消自动续费(小黄车怎么取消实名认证)

  • python是什么语音(python属于什么语言?)

    python是什么语音(python属于什么语言?)

  • 抖音为什么保存失败(抖音为什么保存不了视频)

    抖音为什么保存失败(抖音为什么保存不了视频)

  • 默认网关不在由ip地址和子网掩码定义同一网络段上(网关设置)

    默认网关不在由ip地址和子网掩码定义同一网络段上(网关设置)

  • 合同中增值税税率怎么描述
  • 减免的企业所得税计入什么科目
  • 生产企业出口自产的小汽车
  • 小规模纳税人劳务费税率1%
  • 个体的个人所得税怎么算
  • 坏账损失计入什么
  • 增值税小规模纳税人
  • 什么时候库存商品什么时候主营业务收入
  • 政府补贴营业外收入所得税汇算清缴需要调增吗
  • 生产车间职工工资
  • 固定资产折旧购买日期还是使用日期呢
  • 非工作日是否可以开标
  • 企业之间的借款利息计入什么科目
  • 金税盘发票打印不全
  • 公司购车入固定资产包括哪些
  • 外资企业研发中心
  • 因保管不善,不慎遗失
  • 公司购车按揭利息可以开票吗
  • 公司按揭购车可以抵扣税吗
  • 验资后资金转给第三方
  • 机动车强制保险新政策
  • 企业安排残疾人就业有啥优惠政策
  • 技术咨询合同书
  • 长期驻扎外地的员工
  • 兼职人员工资个税怎么申报
  • 发改委规定电网企业职工福利费
  • 应收账款及票据
  • 畅捷通t3怎么增加新的会计年度
  • 跨期费用会计处理
  • 以前年度资产损失,汇算清缴怎么填表
  • w10系统怎么连接
  • 无票收入科目
  • 复制粘贴快捷键失效了怎么回事
  • 物流运输公司要交税吗
  • 预付款无法收回账务处理
  • 电脑cn是什么意思
  • explorer.exe是啥意思
  • PHP:stream_encoding()的用法_Stream函数
  • 租入经营用房屋的改良支出
  • 出口退税款计入营业外收入要交所得税吗
  • 无票收入怎么计算1%税率
  • 以银行存款支付业务招待费
  • 文心一言中国经济大讲堂
  • 员工福利费怎样计提
  • 进口固定资产支付的关税
  • 收回已核销的坏账并入账是什么意思
  • 建筑企业其他应付款包含什么内容
  • 差旅费过路费会计分录
  • 施工企业的内部往来
  • 软件企业增值税计算
  • 稽查补缴税款会计处理
  • 固定资产清理时增值税怎么处理
  • 房地产开发产品科目设置
  • 什么叫代销合同
  • 小规模免征增值税到什么时间止
  • 购进商品用于销售
  • 投资性房地产转换日的确定
  • 工资计提多了冲账怎么办
  • 在网上如何申领发票
  • 去年管理费用多支三万元怎么办
  • 期末自动结转期间损益
  • sql server发布
  • centos yun
  • mssql数据库的账号密码
  • win7系统安装win10 怎么安装
  • mac os ftp
  • freebsd使用手册
  • 重装系统的简写
  • win8.1玩游戏卡
  • javascript中的数组可以存放任何类型的数据
  • opengl绘制三维图形代码
  • js.ajax
  • 一个方便的世界作文
  • javascript字符类型
  • Android开发网络请求数据加密
  • 国税局和税务稽查的区别
  • 药品定价的三种形式
  • 成都市水源地及饮用水的水质情况
  • 增值税号和增值税账号的区别
  • 广东省国家税务总局稽查局局长
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

    网站地图: 企业信息 工商信息 财税知识 网络常识 编程技术

    友情链接: 武汉网站建设