设计模式-行为型模式-责任链模式

news/2024/5/19 14:24:46 标签: 设计模式, 责任链模式

一、什么是责任链模式

        责任链模式是一种设计模式。在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织和分配责任。(摘自百度百科)

        像上图这个审核流程,从开始到结束,需要五步,这五步组成了一个链路,这个就可以看作责任链,当然,我们可以通过if语句,依次写,但是,这样写出来的代码,不优雅,且后续想再加流程或修改某一地方,当看到一堆的if-else时,已经不想改了……

二、场景模拟

        为了应用责任链模式,我这里模拟一个场景--预约活动场景。首先,预约之前,要检查用户是否有资格预约,或者是这个活动还有没有效,或者……。一堆的检查,看下面这个流程图:

        1、活动有效性检查:我们要看此用户预约的活动,是否还在有效期内等等;

        2、活动规则检查:看用户是否符合此活动的规则,例如仅限新用户啊,或者限男女等等;

        3、剩余容量检查:没剩余容量了,用户就无法预约了。

以上就是我们简单的三个需求,当然后续可以扩增,但这里就不赘述,ctrl c+v而已。

三、代码工程

3.1、工程结构

其中:domian放置一些类信息,例如用户User、活动Maneuver;BookingInfo接收用户发送的预约请求; CheckInfo模拟校验链返回信息;枚举类CheckStatus时一些状态枚举信息。基于以上,我们简单实现以下。

3.2、基础代码

User.class(简单模拟用户)

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {

    // 用户
    private String name;
    // 其他信息
    private String otherInfo;
}

Maneuver.class(简单模拟活动)

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Maneuver {

    // 名称
    private String name;
    // 开始
    private LocalDateTime startTime;
    // 结束
    private LocalDateTime endTime;
    // 容量
    private Long capacity;
    // 其它规则
    private String rule;
}

BookingInfo.class(简单模拟接收用户发送的预约请求)

@Data
@AllArgsConstructor
@NoArgsConstructor
public class BookingInfo {

    // 预约用户名
    private String userName;
    // 预约活动名
    private String maneuverName;
    // 其它信息
    private String otherInfo;
}

CheckStatus.class(简单模拟状态枚举信息)

public enum CheckStatus {

    SUCCESS("10001"),
    ERROR("20001"),
    EXPIRED("30001"),
    NO_MANEUVER("40001"),
    NO_MANEUVER_SEGMENT("40002"),
    VOLUME_LESS("50001");

    private final String code;
    CheckStatus(String code){
        this.code = code;
    }

    public String getCode(){
        return this.code;
    }
}

CheckInfo.class(简单模拟校验返回信息)

@Data
@AllArgsConstructor
@NoArgsConstructor
public class CheckInfo {

    // 状态
    private CheckStatus checkStatus;
    // 描述信息
    private String info;
}

以上,基础代码部分就结束了,接下来,我们来开始模拟业务。

四、业务实现

4.1、使用if-else方式实现

public static void errorDemo(Maneuver maneuver, User user, BookingInfo bookingInfo){

        LocalDateTime now = LocalDateTime.now();
        // 1、检验此活动是否有效;
        if (!now.isBefore(maneuver.getEndTime())) {
            // ...还有其它校验
            log.error("活动过期!");
            return;
        }

        // 2、其它关键规则校验;
        if (!user.getOtherInfo().equals(maneuver.getRule())) {
            // ...还有其它校验
            log.error("不符合规则!");
            return;
        }

        // 3、检验此活动是否有剩余容量;
        if (maneuver.getCapacity() <= 0) {
            // ...还有其它校验
            log.error("剩余容量不足");
            return;
        }

        log.info("预约成功!" + bookingInfo);
    }

然后我们写个测试类,实现一下:

public static void main(String[] args) {

        // 模拟活动
        Maneuver maneuver = new Maneuver("预约活动1",
                LocalDateTime.of(2023, 11, 19, 7, 0),
                LocalDateTime.of(2023, 11, 19, 19, 0),
                1000L,
                "man");

        // 模拟用户
        User user = new User("张三", "man");

        // 模拟预约信息
        BookingInfo bookingInfo = new BookingInfo("张三", "预约活动1", "...");

        // 使用if来判断
        errorDemo(maneuver, user, bookingInfo);

    }

运行结果:

然后我们修改一下User信息模拟一下失败的场景:

可以看到,确实是能实现功能。但是,我这里if代码块里的功能只是做了简化的描述,真实的业务场景校验规则可能有很多很多的代码逻辑,就会导致一堆一堆的if-else堆积在这里,看着就很恶心。所以,我们要用责任链设计模式来优化它!

4.2、使用责任链设计模式重构

4.2.1、责任链模式结构

         首先我们创建一个CheckLink抽象类,新建类去继承CheckLink,实现他的doCheck()方法,这样可以灵活新建校验;且配合next和实现下灵活搭配。

4.2.2、创建责任链抽象类

public abstract class CheckLink {

    private String checkLinkName;  // 此校验链名称
    private CheckLink next;  // 下一个校验链

    public CheckLink(String checkLinkName) {
        this.checkLinkName = checkLinkName;
    }

    public CheckLink setNext(CheckLink next){
        this.next = next;
        return this;
    }

    public CheckLink getNext(){
        return this.next;
    }

    public String getCheckLinkName(){
        return this.checkLinkName;
    }

    public abstract CheckInfo doCheck(User user, Maneuver maneuver, BookingInfo bookingInfo);
}

上面重要的信息只有一个,就是next变量,这个变量指向了下一个责任链。如果下一个责任链为空,说明这就是责任链的最后一个了;如果下一个责任链不为空,那就依次往后执行。

4.2.3、实现类模拟检验规则

首先我们模拟活动校验

@Slf4j
public class ManeuverCheck extends CheckLink {


    public ManeuverCheck(String checkLinkName) {
        super(checkLinkName);
    }

    @Override
    public CheckInfo doCheck(User user, Maneuver maneuver, BookingInfo bookingInfo) {

        log.info(getCheckLinkName() + " start ...");

        LocalDateTime now = LocalDateTime.now();

        // 1、检验此活动是否有效;
        if (!now.isBefore(maneuver.getEndTime())) {
            return new CheckInfo(CheckStatus.EXPIRED, "活动已过期");
        }

        // 如果后面没有了,说明校验通过
        if(getNext() == null){
            return new CheckInfo(CheckStatus.SUCCESS, "校验通过");
        }

        // 如果后面还有校验链,再继续执行
        return getNext().doCheck(user, maneuver, bookingInfo);
    }
}

然后模拟规则校验

@Slf4j
public class RuleCheck extends CheckLink {

    public RuleCheck(String checkLinkName) {
        super(checkLinkName);
    }

    @Override
    public CheckInfo doCheck(User user, Maneuver maneuver, BookingInfo bookingInfo) {

        log.info(getCheckLinkName() + " start ...");

        // 2、其它关键规则校验;
        if (!user.getOtherInfo().equals(maneuver.getRule())) {
            return new CheckInfo(CheckStatus.ERROR, "规则校验失败");
        }

        // 如果后面没有了,说明校验通过
        if(getNext() == null){
            return new CheckInfo(CheckStatus.SUCCESS, "校验通过");
        }

        // 如果后面还有校验链,再继续执行
        return getNext().doCheck(user, maneuver, bookingInfo);
    }
}

然后模拟容量校验

@Slf4j
public class CapacityCheck extends CheckLink {

    public CapacityCheck(String checkLinkName) {
        super(checkLinkName);
    }

    @Override
    public CheckInfo doCheck(User user, Maneuver maneuver, BookingInfo bookingInfo) {

        log.info(getCheckLinkName() + " start ...");

        // 3、检验此活动是否有剩余容量;
        if (maneuver.getCapacity() <= 0) {
            return new CheckInfo(CheckStatus.VOLUME_LESS, "剩余容量不足");
        }

        // 如果后面没有了,说明校验通过
        if(getNext() == null){
            return new CheckInfo(CheckStatus.SUCCESS, "校验通过");
        }

        // 如果后面还有校验链,再继续执行
        return getNext().doCheck(user, maneuver, bookingInfo);
    }
}

当然,有别的校验需求也可以自定义添加,这里就先写三个。让我们来分析一下代码结构,这三个结构都是类似的,看doCheck()方法,首先执行校验内容,如果校验失败,就返回提示信息;如果校验成功,就再判断一下是否处于责任链末端,如果处于责任链末端,说明此责任链已经执行完毕;如果后续还有其他责任链,就传播至下一责任链,以此类推。

4.2.4、运行测试

public static void useResponChainModel(Maneuver maneuver, User user, BookingInfo bookingInfo){
        CheckLink checkLink = new ManeuverCheck("1、活动校验")
                .setNext(new RuleCheck("2、规则校验")
                        .setNext(new CapacityCheck("3、容量校验")));

        CheckInfo checkInfo = checkLink.doCheck(user, maneuver, bookingInfo);

        log.info(String.valueOf(checkInfo));
    }

我们创建一条责任链,依次是:活动校验->规则校验->容量校验,然后执行doCheck()方法,获取CheckInfo信息。

public static void main(String[] args) {

        // 模拟活动
        Maneuver maneuver = new Maneuver("预约活动1",
                LocalDateTime.of(2023, 11, 19, 7, 0),
                LocalDateTime.of(2023, 11, 19, 19, 0),
                1000L,
                "man");

        // 模拟用户
        User user = new User("张三", "man");

        // 模拟预约信息
        BookingInfo bookingInfo = new BookingInfo("张三", "预约活动1", "...");

        // 使用责任链
        useResponChainModel(maneuver, user, bookingInfo);

    }

可以看到运行结果:

成功触发了我们设置的三个校验类,成功完成了校验。我们将条件修改为不符合,再试一下效果:

可以看到修改条件,将规则校验失败的时候,成功停止了后续的校验,并返回了信息。 

至此,我们就完成了使用责任链设计模式重构if-else判断。

五、心得

        之前我做过档案管理系统,刚开始的时候没学习过设计模式,硬是一堆if-else堆上去,因为档案审核流程复杂而且多变,经常因为需求变动重构代码。如果是刚写完的代码重构还方便点,因为还记得怎么写的。一旦时间久了,忘记了之前的业务逻辑,再想在“屎山”上修改代码,太难了。后续,我自己摸索出了一条经验,用自己的语言描述就是,使用“黑箱”,即将每个if-else块都封装在一个“黑箱”里面,一个操作进来,就进黑箱,然后这个黑箱执行一些操作,然后告诉我们结果。我自己尝试了重构了所有的if-else,发现还可以,效果不错。后来,我又尝试在数据库中存储各个流程线的流程,当以后再有任务进来,我就去数据库里读流程,然后再通过“黑箱”依次校验。再后来直接上flowable流程引擎了……

        直到我偶然间听到设计模式,我就发现,我那个“黑箱”就是责任链的雏形。果然,编程思想都是互通的,如果我早点学习设计模式,也不会走这么多弯路。但是想想走弯路也是有好处的,会让自己对责任链模式的认识更加深刻。

        有同学还可能会有这样的疑问,我用if-else,就几行代码,轻松实现;用上设计模式,又要写抽象类,又要写实现类,代码量和复杂性增加了好几倍,图啥?当然,如果是小型项目,可以用if-else,这一点毛病也没用。如果是大型项目且变动多,就可以考虑责任链模式,这个因人而异。我们使用了责任链模式,可以看到代码结构变得清晰了,降低了耦合度,也让对象间的关系变得清晰,所以在后续的开发中,我们可以试着用一下责任链模式


http://www.niftyadmin.cn/n/5192490.html

相关文章

基于R语言平台Biomod2模型的物种分布建模与可视化分析

!](https://img-blog.csdnimg.cn/84e1cc8c7f9b4b6ab60903ffa17d82f0.jpeg#pic_center)

QT基础入门【QSS】QT伪状态类型和实例

1、伪装器类型 伪状态描述:active当部件位于活动窗口中时设置此状态。:adjoins-item当 QTreeView 的 ::branch 邻接到一个项时设置此状态。:alternate当 QAbstractItemView 绘制每一行时,如果 QAbstractItemView::alternatingRowColors() 设置为 true,则为每一行的交替行。:b…

LD文件详解

周末卷起来&#xff0c;讲个知识点很散的LD文件&#xff0c;这东西难度完全看原来的LD文件怎么写&#xff0c;我们顶多就是改改&#xff0c;从零开始写的话&#xff0c;人都要到异世界生活了。 说在前面 它不像代码一样都是要定义的&#xff0c;大部分都是标号&#xff0c;没…

【算法萌新闯力扣】:找到所有数组中消失对数字

力扣热题&#xff1a;找到所有数组中消失对数字 开篇 这两天刚交了蓝桥杯的报名费&#xff0c;刷题的积极性高涨。算上打卡题&#xff0c;今天刷了10道算法题了&#xff0c;题目都比较简单&#xff0c;挑选了一道还不错的题目与大家分享。 题目链接:448.找到所有数组中消失对…

代码随想录二刷 | 数组 | 数组理论基础

代码随想录二刷 &#xff5c; 数组 &#xff5c; 数组理论基础 数组是存放在连续内存空间上的相同类型数据的集合。可以通过下标索引的方式获取到下标对应的数据。 数组的下标都是从0开始的 数组内存空间的地址是连续的 因为数组的在内存空间的地址是连续的&#xff0c;所以我们…

元宇宙3D云展厅应用到汽车销售的方案及特点

为了紧紧抓住年轻消费者的需求&#xff0c;汽车销售行业也正在经历一场深刻的变革。在这个变革的前沿&#xff0c;元宇宙3D汽车展厅作为一项全新技术闪亮登场&#xff0c;打破了传统汽车销售模式的限制&#xff0c;为消费者带来了前所未有的购车体验。 元宇宙3D汽车展厅采用了尖…

【C语言数据结构】单链表

目录 分析顺序表和链表实现单链表打印链表动态申请一个节点尾插头插尾删头删查找值函数单链表在pos位置之前插入x单链表在pos位置之后插入x删除pos位置单链表删除pos位置之后的值释放内存空间 分析顺序表和链表 如下图可以看出顺序表的优点 1.尾插尾删足够快 2.下标随机访问和修…