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

  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. 缺点
    • 类的数量变多了,组链时要注意避免出现环状结构,导致出现死循环。
相关推荐
桂月二二2 分钟前
Java编程中的设计模式:单例模式的深入解析与应用
java·单例模式·设计模式
白露与泡影12 分钟前
2024最新最全面Java复习路线(含P5-P8),已收录 GitHub
java·开发语言·github
狄加山67518 分钟前
C语言(指针基础练习)
java·c语言·算法
卓越软件开发25 分钟前
【Java计算机毕业设计】基于SSM+VUE宠物领养管理系统【源代码+数据库+LW文档+开题报告+答辩稿+部署教程+代码讲解】
java·vue.js·课程设计
一只IT攻城狮33 分钟前
Spring Boot集成Kafka:最佳实践与详细指南
java·spring boot·后端·中间件·kafka
大梦百万秋34 分钟前
Spring Boot 实战:构建一个社交平台 API
java·spring boot·后端
TANGLONG22236 分钟前
【初阶数据结构和算法】八大排序算法之插入排序(直接插入排序、希尔排序及其对比)
java·c语言·数据结构·c++·算法·面试·排序算法
极客先躯39 分钟前
高级java每日一道面试题-2024年12月12日-数据库篇-mysql 深度分页如何优化?
java·数据库·mysql·分区表·使用覆盖索引·使用主键或唯一索引进行分页·使用子查询和 join 操作
y250840 分钟前
《时间和空间复杂度》
java·数据结构·算法
菜是原罪44 分钟前
shell脚本自动发布Java应用
java·开发语言