责任链模式在项目中的实际应用(Java)

1.责任链模式

首先简单介绍一下责任链模式。

  1. 定义:使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。将所有处理者形成一条链,在链中决定哪个对象能够处理请求,并返回结果,不能处理则继续向下传递请求。
  2. 优点:
    1. 将请求和处理分开,请求者不需要知道是谁处理的,处理者可以不用知道请求的全貌。
  3. 缺点:
    1. 性能问题,每个请求从链头遍历到链尾,如果链比较长则性能低下。
    2. 调试问题,属于递归调用,调试不方便。
  4. 在这里不对责任链模式做过多的介绍,简单说明,然后阐述在项目中的应用以及代码的实现。

2.实际业务场景

在我公司的项目中,有一些需要过期处理的东西,比如,订单未支付过期处理,发红包未领取自动退还,代金券未支付过期处理等。实现方式都是采用我之前所写的一篇文章,基于Redis实现订单倒计时自动关闭——Java。在存入redis的时候,会通过key来区分是什么业务,比如订单的key为AA开头,红包的key为BB开头,代金券的key为CC开头。那么,如果监听到redis中有过期的键值对的话,程序会得到键值对的key,通过key来判断属于哪部分业务,然后进行相应的处理。传统做法为,将所有的处理业务都写在一个类,或者一个方法中,通过多个if判断执行不同的逻辑,比如,如果是AA开头,那么执行订单的过期处理逻辑等等。

这样的做法并没有问题,我们讨论的只是一种更好的处理方式。

传统的做法会使处理逻辑耦合在一起,变得比较臃肿,复杂,难以维护。而大部分设计模式都是为了增加程序的可维护性以及可扩展性,或者说解耦。

在这个场景中,应用了责任链模式,相当于我把每一个处理逻辑都看作是一个独立的处理者,然后将他们形成一个处理链,这样当一个请求进来之后,通过遍历这个处理链来处理请求。在遍历链时,通过判断,如果能处理该请求,则处理并返回,不在继续向下遍历,如果不能处理则继续向下遍历,交个下一个处理者。

3.具体代码实现

通过上面的描述,我们知道,所有的处理者都是独立的,并且每个处理者应该都具备相同的行为----处理过期逻辑,首先我通过一个抽象类,定义出抽象的处理者。

public abstract class AbstractRedisExpireHandle {

    //key的前缀,用于区分是什么业务
    private String prefix;

    //构造,子类必须实现
    public AbstractRedisExpireHandle(){}

    /**
     * 操作前缀属性的公开方法
     * @return
     */
    public String getPrefix() {
        return prefix;
    }

    public void setPrefix(String prefix) {
        this.prefix = prefix;
    }

    //抽象的,所有具体处理者应该实现的处理逻辑
    abstract void expireHandle(String redisKey);

}

接下来看具体的处理者,所有的具体处理者都要继承抽象处理者,具体处理者之间是完成独立的,解耦的。

/**
 * 商品订单过期
 */
@Component
public class GoodsOrderExpireHandle extends AbstractRedisExpireHandle {

    //设置该业务的前缀
    private static final String PREFIX = "dd";

    public GoodsOrderExpireHandle(){
        setPrefix(PREFIX);
    }

    @Reference(version = "1.0.0")
    private TaskService taskService;

    //实现具体处理逻辑
    @Override
    void expireHandle(String redisKey) {
        taskService.goodsOrderExpireHandle(redisKey);
    }
}
/**
 * 红包过期
 */
@Component
public class RedPacketExpireHandle extends AbstractRedisExpireHandle {

    //设置业务前缀
    private static final String PREFIX = "kb";

    @Reference(version = "1.0.0")
    private TaskService taskService;

    public RedPacketExpireHandle(){
        setPrefix(PREFIX);
    }

    //实现具体处理逻辑
    @Override
    void expireHandle(String redisKey) {
        taskService.redPacketExpireHandle(redisKey);
    }
}

所有的具体处理者定义完后,需要将他们组成一个处理链,并且进行工作,接下来看如何将所有的相关具体处理者组成一个处理链,因为项目是基于spring的,所以这里使用了spring提供的几个相关接口,并且在spring容器启动的时候完成处理链的创建。

@Component
public class ExecuteHandle implements ApplicationContextAware,InitializingBean {

    //spring容器
    private ApplicationContext context;

    //具体处理者的集合
    private List<AbstractRedisExpireHandle> expireHandles = new ArrayList<>();

    //该方法会在容器启动的时候被调用
    @Override
    public void afterPropertiesSet() throws Exception {
        //从容器中找到所有继承了抽象处理者的类,并加入到集合中,从而形成处理链
        String[] beanNames =  BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context,AbstractRedisExpireHandle.class);
        for(int i=0;i<beanNames.length;i++){
            expireHandles.add((AbstractRedisExpireHandle)context.getBean(beanNames[i]));
        }
    }

    //该方法会将spring的当前容器传递进来
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.context = applicationContext;
    }


    //遍历处理链,通过前缀来判断,能否处理逻辑,如果不能则继续遍历
    public void handle(String redisKey){
        if(expireHandles.size() > 0){
            for(AbstractRedisExpireHandle abstractRedisExpireHandle : expireHandles){
                if(redisKey.startsWith(abstractRedisExpireHandle.getPrefix())){
                    abstractRedisExpireHandle.expireHandle(redisKey);
                }
            }
        }
    }

}

上述代码比较简单,该类实现了spring的两个接口,一个是ApplicationContextAware和InitializingBean。

ApplicationContextAware为容器感知接口,实现这个接口需要实现setApplicationContext方法,spring会将容器当做参数传递到这个方法中,我们就可以使用当前容器了。

InitializingBean接口为初始化Bean的接口,实现这个接口需要实现afterPropertiesSet方法,该方法会在spring实例化这个类的时候被调用,可以做一些初始化工作,我就是在这个方法中将所有的具体处理者形成处理链的。

最后,通过遍历整个处理链,来处理相关的逻辑,结合之前所写的文章,使用该处理链处理请求的代码也十分简单,只需要调用上面的handle方法即可。

@Component
public class RedisExpireListener implements MessageListener {

    @Autowired
    private ExecuteHandle executeHandle;

    @Override
    public void onMessage(Message message, byte[] bytes) {
        byte[] body = message.getBody();
        String redisKey = new String(body);
        executeHandle.handle(redisKey);
    }
}

4.总结

这样做之后,我们将所有具体处理者都进行了解耦,互相之间毫无关系。那么后续如果在有新的处理逻辑需要加入进来的话,只需要继承抽象处理者,实现相关的处理逻辑,具体处理者就可以进行工作了,无需修改其他任何代码。

这种方式个人觉得还是比较好的,当然肯定会有更好的处理方式,只不过目前我还没有发掘到,大家如果有其他的好的建议可以一起讨论。

  • 7
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值