• 基于java实现责任链进行参数校验


    责任链是什么

    在Java中,责任链模式(Chain of Responsibility Pattern)是一种行为设计模式,它允许对象以链式的方式组织起来,以便请求可以在链中传递,直到被某个对象处理为止。这种模式为多个对象处理同一请求提供了灵活的机制,并且可以将请求的处理者与请求的发送者解耦。

    责任链优点

    降低耦合度:该模式将请求的发送者和多个请求处理者解耦,发送者只需要将请求发送到链上,而不需要知道具体是哪个对象处理了请求。

    增强系统的可扩展性:你可以很容易地添加新的请求处理者到链中,而不需要修改现有的代码。

    灵活性:每个处理者都可以独立地决定是否处理请求,或者将请求传递给链中的下一个处理者。

    避免多重条件判断:如果没有使用责任链模式,我们可能会使用多重if-else或者switch-case语句来根据条件判断请求应该由哪个处理者处理。这会导致代码的可读性和可维护性下降。而使用责任链模式,可以避免这种复杂的多重条件判断。

    责任链使用场景

    责任链模式在工作中的具体使用场景非常广泛,
    在一些开源框架源码中,比如sentinel,gateway都应用了责任链模式,它特别适用于那些需要动态确定请求处理者、处理者不明确或处理者集合需要动态指定的场景。以下是一些实际工作中的使用场景示例:

    审批流程:在一个复杂的审批系统中,一个请求可能需要经过多个审批人员或部门的审批。每个审批人员或部门都可以看作责任链中的一个节点,他们根据各自的职责和规则来处理请求,直到请求被批准或拒绝。通过责任链模式,可以灵活地配置审批流程,并动态地添加、删除或修改审批节点。

    权限校验:在权限管理系统中,当用户尝试访问某个资源或执行某个操作时,需要进行权限校验。责任链模式可以将不同的权限校验逻辑封装到不同的处理器中,并按照一定的顺序组成责任链。每个处理器可以根据用户的角色、权限等信息来判断是否允许访问或执行操作。这样,可以方便地添加新的权限校验规则或修改现有的规则。

    任务分发:在分布式系统中,任务分发是一个重要的环节。责任链模式可以用于将任务按照一定的规则和顺序分发给不同的处理节点。每个处理节点可以看作责任链中的一个处理器,它们根据任务的类型、优先级、负载情况等因素来决定是否接受任务并进行处理。这种方式可以提高系统的吞吐量和响应速度,实现任务的动态均衡分配。

    事件处理:在事件驱动的系统中,当某个事件发生时,需要触发相应的处理逻辑。责任链模式可以将不同的事件处理逻辑封装到不同的处理器中,并按照一定的顺序组成责任链。当事件发生时,它会沿着责任链传递,直到找到能够处理该事件的处理器为止。这种方式可以灵活地处理各种事件,并实现事件的动态扩展和配置。

    插件式架构:在插件式架构中,不同的插件可以提供不同的功能或服务。责任链模式可以用于将插件按照一定的顺序连接起来,形成一个功能强大的处理链。每个插件可以看作责任链中的一个处理器,它们根据请求的类型和内容来执行相应的操作。这种方式可以提高系统的可扩展性和可维护性,方便添加新的插件或修改现有的插件。

    总的来说,责任链模式在工作中可以帮助我们实现请求的动态处理和灵活的扩展,提高系统的可维护性和可扩展性。它可以应用于各种需要动态确定处理者、处理者不明确或处理者集合需要动态指定的场景,为复杂系统的设计和实现提供了有力的支持。

    责任链实现细节

    项目布局
    在这里插入图片描述
    AbstractChain

    package com.goods.chain;
    
    import lombok.extern.slf4j.Slf4j;
    
    import java.util.function.Predicate;
    
    /**
     * Copyright 2022 skyworth
     *
     * @Author: wuhao
     * @CreateTime: 2024-04-23 11:23
     * @Description: AbstractChain
     * @Version: 1.0
     **/
    @Slf4j
    public abstract class AbstractChain<T> implements Predicate<T> {
    
        public abstract boolean validate(T o);
    
        @Override
        public boolean test(T o) {
            if (validate(o)) {
                return true;
            } else {
                log.error("被规则类『" + this.getClass().getCanonicalName() + "』过滤住了!");
            }
            return false;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    AddressChain

    package com.goods.chain;
    
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.lang.StringUtils;
    import org.springframework.core.annotation.Order;
    import org.springframework.stereotype.Component;
    
    /**
     * Copyright 2022 skyworth
     *
     * @Author: wuhao
     * @CreateTime: 2024-04-23 11:13
     * @Description: SecondChain
     * @Version: 1.0
     **/
    @Slf4j
    @Component
    @Order(2)
    public class AddressChain extends AbstractChain<GoodsDto> {
        @Override
        public boolean validate(GoodsDto dto) {
            if(StringUtils.isBlank(dto.getAddress())){
                log.error("收货地址不能为空!");
                return false;
            }
            return true;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    ChainClient

    package com.goods.chain;
    
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.lang.BooleanUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    import java.util.List;
    
    /**
     * Copyright 2022 skyworth
     *
     * @Author: wuhao
     * @CreateTime: 2024-04-23 23:43
     * @Description: ChainClient
     * @Version: 1.0
     **/
    @Slf4j
    @Component
    public class ChainClient {
    
        @Autowired
        private List<AbstractChain<GoodsDto>> chainList;
    
        public boolean handleChain(GoodsDto dto) {
            boolean result = true;
            for (AbstractChain<GoodsDto> chain : chainList){
                if(BooleanUtils.isFalse(chain.test(dto))){
                    result = false;
                    break;
                }
            }
            return result;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    DiscountChain

    package com.goods.chain;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.core.annotation.Order;
    import org.springframework.stereotype.Component;
    
    import java.util.Objects;
    
    /**
     * Copyright 2022 skyworth
     *
     * @Author: wuhao
     * @CreateTime: 2024-04-23 11:12
     * @Description: FirstChain
     * @Version: 1.0
     **/
    @Slf4j
    @Component
    @Order(1)
    public class DiscountChain extends AbstractChain<GoodsDto> {
        @Override
        public boolean validate(GoodsDto dto) {
            if(Objects.nonNull(dto.getDiscount()) && dto.getDiscount() <1){
                log.error("折扣小于1折,校验失败!");
                return false;
            }
            return true;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    GoodsDto

    package com.goods.chain;
    
    import lombok.Builder;
    import lombok.Data;
    
    /**
     * Copyright 2022 skyworth
     *
     * @Author: wuhao
     * @CreateTime: 2024-04-23 18:36
     * @Description: GoodsDTO 商品skuDTO
     * @Version: 1.0
     **/
    @Data
    @Builder
    public class GoodsDto {
    
        /**
         * 商品名称
         */
        private String goodsName;
    
        /**
         * 商品价格,单位分
         */
        private Long price;
    
        /**
         * 下单数量
         */
        private Integer count;
    
        /**
         * 重量,单位g
         */
        private Integer weight;
    
        /**
         * 收货地址
         */
        private String address;
    
        /**
         * 折扣
         */
        private Integer discount;
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    PriceChain

    package com.goods.chain;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.core.annotation.Order;
    import org.springframework.stereotype.Component;
    
    import java.util.Objects;
    
    /**
     * Copyright 2022 skyworth
     *
     * @Author: wuhao
     * @CreateTime: 2024-04-23 11:14
     * @Description: ThirdChain
     * @Version: 1.0
     **/
    @Slf4j
    @Component
    @Order(3)
    public class PriceChain extends AbstractChain<GoodsDto> {
        @Override
        public boolean validate(GoodsDto dto) {
            if (Objects.isNull(dto.getPrice()) || dto.getPrice() < 1L) {
                log.error("商品定价不能为空或小于1分钱!");
                return false;
            }
            return true;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    TestController

    package com.goods.chain;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    /**
     * Copyright 2022 skyworth
     *
     * @Author: wuhao
     * @CreateTime: 2024-04-23 11:15
     * @Description: Test
     * @Version: 1.0
     **/
    @Slf4j
    @RestController
    public class TestController {
        @Autowired
        private ChainClient chainClient;
    
        /**
         * 责任链测试
         * @return
         */
        @RequestMapping("/test/chain")
        public String testChain() {
            GoodsDto dto = GoodsDto.builder()
                    .weight(100)
                    .goodsName("macBookPro")
                    .address("北京市朝阳区亮马桥汇佳大厦")
                    .price(0L)
                    .count(1)
                    .discount(1)
                    .build();
            boolean result = chainClient.handleChain(dto);
            log.info("责任链执行完成,校验结果:{}",result);
            return "hello";
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    模拟使用的场景
    在这里插入图片描述

  • 相关阅读:
    shell变量
    5. hdfs的界面详解
    xcode Simulator 手动安装
    微信小程序的框架
    前端面试宝典
    一起学时序分析之建立/保持时间裕量
    被所有大厂嫌弃,终被字节收了,历时3个月的软件测试技术沉淀分享
    C. Make Good
    C++基础知识梳理<2>(引用、内联函数、auto关键字) [入门级】
    footer页面布局
  • 原文地址:https://blog.csdn.net/mikewuhao/article/details/138155062