行为型设计模式之《责任链模式》实践

  1. 定义
    责任链模式(Chain Of Responsibility Pattern)顾名思义,就是为请求创建一条处理链路,链路上的每个处理器都判断是否可以处理请求,如果不能处理则往后走,依次从链头走到链尾,直到有处理器可以处理请求。
  2. 类型
    2.1 请求只处理一次
    每个节点都有机会处理请求,但是请求只要处理成功就结束了。

场景

流程审批、扑克牌

代码示例

原来

java 复制代码
public class Apply {
    public boolean apply(int requireDay) {
        if (requireDay <= 1) {
            return true;
        } else if (requireDay <= 3) {
            applyByLeader(requireDay);
        } else {
            applyByManager(requireDay);
        }
    }
    public boolean applyByLeader(int requireDay) {
        //...
    }
    public boolean applyByManager(int requireDay) {
        //...
    }
}

改造后

BaseHandler.java

java 复制代码
public abstract class BaseHandler {
    protected BaseHandler successor;
    public void setSuccessor(BaseHandler successor) {
        this.successor = successor;
    }
    public abstract boolean apply(int requireDay);
    public void print() {
        System.out.println(this.getClass().getSimpleName() + " process.");
    }
}

AutoHandler.java

java 复制代码
public class AutoHandler extends BaseHandler {
    @Override
    public boolean apply(int requireDay) {
        super.print();
        if (requireDay <= 1) {
            return true;
        }
        return successor.apply(requireDay);
    }
}

LeaderHandler.java

java 复制代码
public class LeaderHandler extends BaseHandler {
    
    @Override
    public boolean apply(int requireDay) {
        super.print();
        if (requireDay <= 3) {
            return true;
        }
        return successor.apply(requireDay);
    }
}

ManagerHandler.java

java 复制代码
public class ManagerHandler extends BaseHandler {
    private static final int MAX_DAY = 996;
    
    @Override
    public boolean apply(int requireDay) {
        super.print();
        return requireDay <= MAX_DAY;
    }
}

HandlerClient.java

java 复制代码
public class HandlerClient {
    private static final AutoHandler AUTO_HANDLER;
    static {
        ManagerHandler managerHandler = new ManagerHandler();
        LeaderHandler leaderHandler = new LeaderHandler();
        leaderHandler.setSuccessor(managerHandler);
        AUTO_HANDLER = new AutoHandler();
        AUTO_HANDLER.setSuccessor(leaderHandler);
    }
    public static void main(String[] args) {
        AUTO_HANDLER.apply(1);
        System.out.println();
        AUTO_HANDLER.apply(3);
        System.out.println();
        AUTO_HANDLER.apply(5);
    }
}

输出:

java 复制代码
AutoHandler process.
AutoHandler process.
LeaderHandler process.
AutoHandler process.
LeaderHandler process.
ManagerHandler process.

2.2 请求处理多次

每个节点都有机会处理请求,节点处理完之后继续往后走,直到链尾。

场景

• 过滤器 / 拦截器

• JavaEE 的 Servlet 规范定义的 Filter

代码示例

请求如果成功通过 process 处理,则进入下一个 process,如果不通过则被过滤掉,这里不再累述代码。

  1. 项目实践

有个根据配置构造ODPS查询语句的代码,配置片段如下:

json 复制代码
{
  "name": "Document no",
  "code": "service_order_code",
  "isBasicField": true,
  "fromRerating": false,
  "classType": "java.lang.String"
}

原来是通过 if-else 来实现的,代码如下所示:

现在要新增非空校验的字段 notNull,现在配置如下:

java 复制代码
{
  "name": "Document no",
  "code": "service_order_code",
  "isBasicField": true,
  "fromRerating": false,
  "classType": "java.lang.String",
  "notNull": true
}

发现又得往 if-else 里面硬塞分支,有代码洁癖的我怎么能容忍自己写这种代码?最近也从同事那里了解到责任链模式的厉害之处,索性直接给它优化掉,这里我截取下关键代码片段。

首先声明抽象处理类

java 复制代码
/**
 * 责任链抽象处理器
 */
public abstract class AbstractHandler<T, V> {
    protected AbstractHandler<T, V> successor;
    public void setSuccessor(AbstractHandler<T, V> handler) {
        this.successor = handler;
    }
    /**
     * 处理方法
     *
     * @param context 上下文
     * @return R
     */
    public abstract String process(Context<T, V> context);
}

各具体处理类安排上

java 复制代码
/**
 * 责任链入口,扩展字段处理
 */
public class FirstExtendFieldHandler extends AbstractHandler<AdjustmentTemplateDTO, String> {
    /**
     * 处理方法
     *
     * @param context 上下文
     * @return R
     */
    @Override
    public String process(Context<AdjustmentTemplateDTO, String> context) {
        AdjustmentTemplateDTO request = context.getRequest();
        if (!request.getBasicField()) {
            String tmpField = request.getFromRerating() ? String.format(GET_JSON_OBJECT, NEW_PARAM, PREFIX + request.getCode()) :
                String.format(GET_JSON_OBJECT, OLD_PARAM, PREFIX + request.getCode());
            context.setResult(tmpField);
        }
        return successor.process(context);
    }
}
/**
 * 非空字段处理器
 */
public class SecondNotNullFieldHandler extends AbstractHandler<AdjustmentTemplateDTO, String> {
    /**
     * 处理方法
     *
     * @param context 上下文
     * @return R
     */
    @Override
    public String process(Context<AdjustmentTemplateDTO, String> context) {
        AdjustmentTemplateDTO request = context.getRequest();
        if (!request.getBasicField()) {
            return successor.process(context);
        }
        if (request.getNotNull() == null || !request.getNotNull()) {
            String tmpField = (request.getFromRerating() ? NEW : OLD) + request.getCode();
            context.setResult(tmpField);
            return successor.process(context);
        } else {
            String oldValue = OLD + request.getCode();
            context.setResult(oldValue);
            oldValue = successor.process(context);
            String newValue = NEW + request.getCode();
            context.setResult(newValue);
            newValue = successor.process(context);
            String finalField = String.format(OdpsConstants.IF, oldValue, newValue, oldValue);
            context.setResult(finalField);
            return finalField;
        }
    }
}
 /**
 * 时间字段处理
 */
public class ThirdTimeFormatHandler extends AbstractHandler<AdjustmentTemplateDTO, String> {
    @Override
    public String process(Context<AdjustmentTemplateDTO, String> context) {
        AdjustmentTemplateDTO request = context.getRequest();
        String finalSql = StringUtils.isNotBlank(request.getTimeFormat())
            ? String.format(TIMESTAMP_FORMAT, context.getResult(), request.getTimeFormat())
            : context.getResult();
        context.setResult(finalSql);
        return finalSql;
    }
}

最后是负责初始化责任链的客户端

java 复制代码
/**
 * 责任链客户端
 */
public class HandlerChainClient {
    private static final FirstExtendFieldHandler FIRST_HANDLER;
    static {
        ThirdTimeFormatHandler thirdHandler = new ThirdTimeFormatHandler();
        SecondNotNullFieldHandler secondHandler = new SecondNotNullFieldHandler();
        secondHandler.setSuccessor(thirdHandler);
        FIRST_HANDLER = new FirstExtendFieldHandler();
        FIRST_HANDLER.setSuccessor(secondHandler);
    }
    /**
     * 调用责任链进行处理
     *
     * @param request 请求参数
     * @return result
     */
    public static String process(AdjustmentTemplateDTO request) {
        AdjustmentContext context = new AdjustmentContext();
        context.setRequest(request);
        return FIRST_HANDLER.process(context);
    }
}

最后,业务代码里又臭又长的 if-else 变成了一行代码。

java 复制代码
HandlerChainClient.process(request);
  1. 优点
    • 解耦。请求发送者无需知道请求在何时、何处以及如何被处理,实现了发送者与处理者的解耦。
    • 灵活、可插拔。可以看到想要添加一个处理流程,只需实现BaseHandler,然后注入到对应的位置即可;删除一个流程也是一样,只需要将本节点的位置替换成下一个节点即可,客户端无需感知处理器的变化。
    • 代码优雅,责任链相比 if-else 是更加优雅的。
  2. 缺点
    • 类的数量变多了,组链时要注意避免出现环状结构,导致出现死循环。
相关推荐
腥臭腐朽的日子熠熠生辉27 分钟前
解决maven失效问题(现象:maven中只有jdk的工具包,没有springboot的包)
java·spring boot·maven
ejinxian29 分钟前
Spring AI Alibaba 快速开发生成式 Java AI 应用
java·人工智能·spring
杉之35 分钟前
SpringBlade 数据库字段的自动填充
java·笔记·学习·spring·tomcat
圈圈编码1 小时前
Spring Task 定时任务
java·前端·spring
俏布斯1 小时前
算法日常记录
java·算法·leetcode
27669582921 小时前
美团民宿 mtgsig 小程序 mtgsig1.2 分析
java·python·小程序·美团·mtgsig·mtgsig1.2·美团民宿
爱的叹息1 小时前
Java 连接 Redis 的驱动(Jedis、Lettuce、Redisson、Spring Data Redis)分类及对比
java·redis·spring
程序猿chen1 小时前
《JVM考古现场(十五):熵火燎原——从量子递归到热寂晶壁的代码涅槃》
java·jvm·git·后端·java-ee·区块链·量子计算
松韬2 小时前
Spring + Redisson:从 0 到 1 搭建高可用分布式缓存系统
java·redis·分布式·spring·缓存
绝顶少年2 小时前
Spring Boot 注解:深度解析与应用场景
java·spring boot·后端