天涯论坛

 找回密码
 立即注册
搜索
查看: 66|回复: 3

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

[复制链接]

3061

主题

3万

回帖

9913万

积分

论坛元老

Rank: 8Rank: 8

积分
99139052
发表于 2024-8-4 13:04:10 | 显示全部楼层 |阅读模式

点击上方“芋道源码”,选取设为星标

管她前浪,还是后浪?

能浪的浪,才是好浪!

每日 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

那样代码能够这般

//第1public 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"null2

)),

BLACKLIST_HANDLER(new GatewayEntity(2"黑名单拦截""cn.dgut.design.chain_of_responsibility.GateWay.impl.BlacklistGatewayHandler"13

)),

    SESSION_HANDLER(new GatewayEntity(3"用户会话拦截""cn.dgut.design.chain_of_responsibility.GateWay.impl.SessionGatewayHandler"2null

)),

    ;

    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 领取,更加多内容持续奉上。

文案帮忙的话,在看,转发吧。

谢谢支持哟 (*^__^*)





上一篇:谷歌SEO推广:建设文案内链的10点技巧
下一篇:对话《区块链十年》新书作者:身在历史中,咱们都是写故事的人
回复

使用道具 举报

0

主题

392

回帖

1

积分

新手上路

Rank: 1

积分
1
发表于 2024-9-25 07:20:14 | 显示全部楼层
软文发布平台 http://www.fok120.com/
回复

使用道具 举报

3070

主题

3万

回帖

9915万

积分

论坛元老

Rank: 8Rank: 8

积分
99158931
发表于 2024-11-7 01:26:43 | 显示全部楼层
i免费外链发布平台 http://www.fok120.com/
回复

使用道具 举报

2986

主题

3万

回帖

9956万

积分

论坛元老

Rank: 8Rank: 8

积分
99569167
发表于 5 小时前 | 显示全部楼层
期待更新、坐等、迫不及待等。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

站点统计|Archiver|手机版|小黑屋|天涯论坛 ( 非经营性网站 )|网站地图

GMT+8, 2024-11-22 23:26 , Processed in 0.128268 second(s), 21 queries .

Powered by Discuz! X3.4

Copyright © 2001-2023, Tencent Cloud.