掘金 后端 ( ) • 2024-04-22 16:49

一、什么是责任链

责任链(Chain of Responsibility)是一种设计模式,主要用于避免请求发送者与接收者之间的耦合关系。它通过把请求的发送和处理分割开来,指派给多个对象均可处理的情况下,将这些对象连接成链,并将请求沿链传递,直到有一个对象处理它为止。

责任链模式通常在面向对象的软件设计中使用,例如事件处理框架、Java的异常处理等都使用了这种模式。它对于大规模的、复杂的代码尤其有效,可以简化代码结构和流程,提高代码的可维护性和可读性。

责任链.png

如图所示,我有一个策略工厂,分别有三个策略ABC,但是他们的执行步骤有相同的部分也有不同的部分,我就可以用策略工厂+责任链的模式来写代码。将合适的模块抽象起来复用,职责分明,以后想在方法中间添加新的执行方法也非常方便。

二、通用责任链模板

2.1 共享上下文型

这种类型的责任链在日常工作中用得比较多,我们把方法的入参和结果都放在上下文中方便传递。在责任链中添加和删除方法都比较容易。

2.1.1 前置准备的代码

2.1.1.1 责任链执行方法存放类

public class HandlerChain {
    
    /**
     * 过滤连,希望是能够指定处理逻辑顺序的过滤连
     */
    private List<AbstractHandlerChain> chains = Lists.newArrayList();
    
    
    public void handle(HandlerChainContext context) {
        for (AbstractHandlerChain filterChain : chains){
            if(VerifyUtil.validHandlerChainContext(context)){
                filterChain.handle(context);
            }
        }
    }
    
    
}

2.1.1.2 参数校验类

public class VerifyUtil {
    /**
     * 校验参数是否有效
     **/
    public static boolean validHandlerChainContext(HandlerChainContext context){
        if(参数校验){
            log.warn("validHandlerChainContext param is null. context:{}", JSON.toJSONString(context));
            return false;
        }
        // 标志位判断是否要执行下一步
        return context.isStatus();
    }
}

2.1.1.3 责任链上下文参数

public class HandlerChainContext{

    /**
     * 数据组装dto,这里其实就是把每个具体的方法所需的入参和结果类存放的地方
     **/
    private Bo bo;
    
    /**
     * 责任链执行完了,最后要的返回结果
     */
    private Result result;
    
    /**
     * 状态位,责任链是否要继续执行下一步
     */
    private boolean status;
    
}

2.1.1.4 每个要加入责任链方法的父类

public abstract class AbstractHandlerChain {
    
    /**
     * 处理逻辑
     **/
    public abstract void handle(HandlerChainContext context);

}
2.1.1.4.1 具体的实现子类方法
@Service
public class 方法1Chain extends AbstractHandlerChain{
    
    @Override
    public void handle(HandlerChainContext handlerChainContext) {
        // 入参获取
        Bo bo = handlerChainContext.getBo();
        ··· 代码逻辑
        // 
        if(代码执行是否有异常或者结果不满足返回的要去){
            handlerChainContext.setStatus(false);
            return;
        }
       
        // 返回结果组装
        bo.setAResult(AResult);
    }

2.1.2 责任链执行代码

        // 构造请求处理器的上下文参数
        HandlerChainContext context = new HandlerChainContext();
        context.setBo(new Bo());
        context.setStatus(true);
        
        
        // 放入执行步骤
        HandlerChain handlerChain = new HandlerChain();
        HandlerChain.getChains().add(方法1Chain);
        HandlerChain.getChains().add(方法2Chain);
        HandlerChain.getChains().add(方法3Chain);
        HandlerChain.getChains().add(方法4Chain);
        
        
        // 责任链开始执行
        handlerChain.handle(context);
        // 返回最后的结果
        return context.getResult();

2.2 入参与结果依次传递型

相比把方法的入参和返回结果都放到上下文中。这种方式常用于你非常确认你的责任链中的方法执行顺序基本上固定的,而且上一个方法的返回结果一定是下一个方法的入参。

缺点:否则不要用这种方式,不然后期想在责任链的方法中插入其他方法要改的地方很多。首先关联的上下游方法要改。

优点:把臃肿的上下文给去掉了,实现代码少比较优雅。

考虑到每一步的入参和出参都可能不同,使用单一的 Function<T, R> 可能不够以支持这个需求。我们可能需要创建一个类似于 Function 的新接口,但支持泛型链的概念。例如,可以创建一个链式调用的接口,每个节点在链中持有下一个节点的引用,然后执行时将结果传给下一个节点。

这里是一个简单的示例:

interface ProcessingNode<I, O> {
    O process(I input);

    void setNextNode(ProcessingNode<O, ?> nextNode);
}

abstract class AbstractProcessingNode<I, O> implements ProcessingNode<I, O> {
    protected ProcessingNode<O, ?> nextNode;

    @Override
    public void setNextNode(ProcessingNode<O, ?> nextNode) {
        this.nextNode = nextNode;
    }
}

class FunctionA extends AbstractProcessingNode<Integer, String> {
    @Override
    public String process(Integer input) {
        String result = "Length of " + input + " is " + String.valueOf(input).length();
        if (nextNode != null) {
            nextNode.process(result);
        }
        return result;
    }
}

class FunctionB extends AbstractProcessingNode<String, Double> {
    @Override
    public Double process(String input) {
        Double result = Double.valueOf(input.length());
        if (nextNode != null) {
            nextNode.process(result);
        }
        return result;
    }
}

// More functions...

class FunctionChainRunner {
    public static void main(String[] args) {
        FunctionA functionA = new FunctionA();
        FunctionB functionB = new FunctionB();
        // More function instances...

        functionA.setNextNode(functionB);
        // Link more function instances...

        functionA.process(123);
    }
}
每个具体的函数类(比如 FunctionA 和 FunctionB)都继承了 AbstractProcessingNode 并实现了对应的 process 方法,这些方法内部负责处理输入以及将结果传递给链中的下一个节点。

最后,在 FunctionChainRunner 类的 main 方法中,我们创建了各个函数实例并将他们串联起来,然后启动处理链。

这就是一种针对不同输入和输出类型的函数链的泛型实现策略。