当前位置: 首页 > news >正文

责任链模式让我的代码精简10倍?

目录

  • 什么是责任链
  • 使用场景
  • 结语
0e353d417fb38e23f8467b6b783a9d69.jpeg

前言

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


什么是责任链


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


使用场景


责任链的使用场景还是比较多的:
  • 多条件流程判断:权限控制
  • 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();
            }
        }
    }
}
那么如果这个游戏有 100 关,我们的代码很可能就会写成这个样子: if(第 1关通过){
     // 第2关 游戏
     if(第 2关通过){
         // 第3关 游戏
         if(第 3关通过){
            // 第4关 游戏
             if(第 4关通过){
                 // 第5关 游戏
                 if(第 5关通过){
                     // 第6关 游戏
                     if(第 6关通过){
                         //...
                    }
                }
            } 
        }
    }
}
这种代码不仅冗余,并且当我们要将某两关进行调整时会对代码非常大的改动,这种操作的风险是很高的,因此,该写法非常糟糕。

初步改造

如何解决这个问题,我们可以通过链表将每一关连接起来,形成责任链的方式,第一关通过后是第二关,第二关通过后是第三关....这样客户端就不需要进行多重 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)角色: 创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。
bfb1d7f534bdcf6be32041dc624f4989.jpeg
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();

    }
}

责任链工厂改造

对于上面的请求链,我们也可以把这个关系维护到配置文件中或者一个枚举中。我将使用枚举来教会大家怎么动态的配置请求链并且将每个请求者形成一条调用链。 fd5ecb4668bd2dab2ca35a282876c041.jpeg
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中,方便获取
     */

&nbsp;&nbsp;&nbsp;&nbsp; private&nbsp; static&nbsp;Map<Integer,&nbsp;GatewayEntity>&nbsp;gatewayEntityMap&nbsp;=&nbsp; new&nbsp;HashMap<>();

&nbsp;&nbsp;&nbsp;&nbsp; static&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GatewayEnum[]&nbsp;values&nbsp;=&nbsp;GatewayEnum.values();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for&nbsp;(GatewayEnum&nbsp;value&nbsp;:&nbsp;values)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GatewayEntity&nbsp;gatewayEntity&nbsp;=&nbsp;value.getGatewayEntity();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gatewayEntityMap.put(gatewayEntity.getHandlerId(),&nbsp;gatewayEntity);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp; @Override
&nbsp;&nbsp;&nbsp;&nbsp; public&nbsp;GatewayEntity&nbsp;getGatewayEntity(Integer&nbsp;handlerId)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return&nbsp;gatewayEntityMap.get(handlerId);
&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp; @Override
&nbsp;&nbsp;&nbsp;&nbsp; public&nbsp;GatewayEntity&nbsp;getFirstGatewayEntity()&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for&nbsp;(Map.Entry<Integer,&nbsp;GatewayEntity>&nbsp;entry&nbsp;:&nbsp;gatewayEntityMap.entrySet())&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GatewayEntity&nbsp;value&nbsp;=&nbsp;entry.getValue();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;&nbsp;没有上一个handler的就是第一个
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if&nbsp;(value.getPreHandlerId()&nbsp;==&nbsp; null)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return&nbsp;value;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return&nbsp; null;
&nbsp;&nbsp;&nbsp;&nbsp;}
}

public&nbsp; class&nbsp;GatewayHandlerEnumFactory&nbsp;{

&nbsp;&nbsp;&nbsp;&nbsp; private&nbsp; static&nbsp;GatewayDao&nbsp;gatewayDao&nbsp;=&nbsp; new&nbsp;GatewayImpl();

&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;提供静态方法,获取第一个handler
&nbsp;&nbsp;&nbsp;&nbsp; public&nbsp;static&nbsp;GatewayHandler&nbsp;getFirstGatewayHandler()&nbsp;{

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GatewayEntity&nbsp;firstGatewayEntity&nbsp;=&nbsp;gatewayDao.getFirstGatewayEntity();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GatewayHandler&nbsp;firstGatewayHandler&nbsp;=&nbsp;newGatewayHandler(firstGatewayEntity);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if&nbsp;(firstGatewayHandler&nbsp;==&nbsp; null)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return&nbsp; null;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GatewayEntity&nbsp;tempGatewayEntity&nbsp;=&nbsp;firstGatewayEntity;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Integer&nbsp;nextHandlerId&nbsp;=&nbsp; null;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GatewayHandler&nbsp;tempGatewayHandler&nbsp;=&nbsp;firstGatewayHandler;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;迭代遍历所有handler,以及将它们链接起来
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while&nbsp;((nextHandlerId&nbsp;=&nbsp;tempGatewayEntity.getNextHandlerId())&nbsp;!=&nbsp; null)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GatewayEntity&nbsp;gatewayEntity&nbsp;=&nbsp;gatewayDao.getGatewayEntity(nextHandlerId);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GatewayHandler&nbsp;gatewayHandler&nbsp;=&nbsp;newGatewayHandler(gatewayEntity);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tempGatewayHandler.setNext(gatewayHandler);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tempGatewayHandler&nbsp;=&nbsp;gatewayHandler;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tempGatewayEntity&nbsp;=&nbsp;gatewayEntity;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;返回第一个handler
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return&nbsp;firstGatewayHandler;
&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp; /**
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;反射实体化具体的处理者
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@param&nbsp;firstGatewayEntity
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;@return
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*/

&nbsp;&nbsp;&nbsp;&nbsp; private&nbsp;static&nbsp;GatewayHandler&nbsp;newGatewayHandler(GatewayEntity&nbsp;firstGatewayEntity)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;获取全限定类名
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;className&nbsp;=&nbsp;firstGatewayEntity.getConference();&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;根据全限定类名,加载并初始化该类,即会初始化该类的静态段
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Class<?>&nbsp;clazz&nbsp;=&nbsp;Class.forName(className);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return&nbsp;(GatewayHandler)&nbsp;clazz.newInstance();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp; catch&nbsp;(ClassNotFoundException&nbsp;|&nbsp;IllegalAccessException&nbsp;|&nbsp;InstantiationException&nbsp;e)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.printStackTrace();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return&nbsp; null;
&nbsp;&nbsp;&nbsp;&nbsp;}


}

public&nbsp; class&nbsp;GetewayClient&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp; public&nbsp;static&nbsp;void&nbsp;main(String[]&nbsp;args)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GetewayHandler&nbsp;firstGetewayHandler&nbsp;=&nbsp;GetewayHandlerEnumFactory.getFirstGetewayHandler();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;firstGetewayHandler.service();
&nbsp;&nbsp;&nbsp;&nbsp;}
}


结语


设计模式有很多,责任链只是其中的一种,我觉得很有意思,非常值得一学。设计模式确实是一门艺术,仍需努力呀!
http://www.lryc.cn/news/166100.html

相关文章:

  • Draw软件安装下载
  • uniapp代码混淆ios上架43问题
  • Linux目录遍历函数
  • 数据库-理论基础
  • 【已解决】src/spt_python.h:14:20: 致命错误:Python.h:没有那个文件或目录
  • 基于Face++网络爬虫+人脸融合算法智能发型推荐程序——深度学习算法应用(含Python及打包exe工程源码)+爬虫数据集
  • Jetson nano嵌入式平台配置ip记录
  • 前端中的跨域请求及其解决方案
  • SpringBoot2.0(mybatis-plus初始使用)
  • 游戏视频录制软件对比,哪款最适合你的需求?
  • 耐蚀合金连续油管最新版 学习记录
  • LoGoNet:基于局部到全局跨模态融合的精确 3D 目标检测
  • Python 多线程、线程池、进程池
  • 深入浅出了解华为端到端交付流程的概念和5个关键点
  • [Linux]进程信号
  • PostgreSQL 数据类型
  • 智慧港口4G+UWB+GPS/北斗RTK人员定位系统解决方案
  • 实时时钟和日历电路芯片MS85163/MS85163M
  • 【Java从入门到精通】这也许就是Java火热的原因吧!
  • zTasker—简洁易用强大的定时热键一体自动化工具,效率倍增器
  • 惊艳时装界!AIGC风暴来袭,从设计到生产的全新体验
  • element -ui table表格内容无限滚动 使用插件vue-seamless-scroll
  • 如何在windows环境下编译T
  • USB接口针式打印机
  • 外贸建站教程步骤有哪些?独立站怎么搭建?
  • useGetState自定义hooks解决useState 异步回调获取不到最新值
  • input子系统框架、外设驱动开发
  • Google Chrome 浏览器以全屏模式打开
  • 安装torch113、cuda116并运行demo【Transformer】
  • 基于scRNA-seq的GRN分析三阴性乳腺癌的肿瘤异质性