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

简介: 责任链模式(Chain Of Responsibility Pattern)顾名思义,就是为请求创建一条处理链路,链路上的每个处理器都判断是否可以处理请求,如果不能处理则往后走,依次从链头走到链尾,直到有处理器可以处理请求。

1. 定义

责任链模式(Chain Of Responsibility Pattern)顾名思义,就是为请求创建一条处理链路,链路上的每个处理器都判断是否可以处理请求,如果不能处理则往后走,依次从链头走到链尾,直到有处理器可以处理请求。

2. 类型

2.1 请求只处理一次

每个节点都有机会处理请求,但是请求只要处理成功就结束了。

场景

流程审批、扑克牌

代码示例

原来

publicclassApply {
publicbooleanapply(intrequireDay) {
if (requireDay<=1) {
returntrue;
        } elseif (requireDay<=3) {
applyByLeader(requireDay);
        } else {
applyByManager(requireDay);
        }
    }
publicbooleanapplyByLeader(intrequireDay) {
//...    }
publicbooleanapplyByManager(intrequireDay) {
//...    }
}

改造后

BaseHandler.java

publicabstractclassBaseHandler {
protectedBaseHandlersuccessor;
publicvoidsetSuccessor(BaseHandlersuccessor) {
this.successor=successor;
    }
publicabstractbooleanapply(intrequireDay);
publicvoidprint() {
System.out.println(this.getClass().getSimpleName() +" process.");
    }
}

AutoHandler.java

publicclassAutoHandlerextendsBaseHandler {
@Overridepublicbooleanapply(intrequireDay) {
super.print();
if (requireDay<=1) {
returntrue;
        }
returnsuccessor.apply(requireDay);
    }
}

LeaderHandler.java

publicclassLeaderHandlerextendsBaseHandler {
@Overridepublicbooleanapply(intrequireDay) {
super.print();
if (requireDay<=3) {
returntrue;
        }
returnsuccessor.apply(requireDay);
    }
}

ManagerHandler.java

publicclassManagerHandlerextendsBaseHandler {
privatestaticfinalintMAX_DAY=996;
@Overridepublicbooleanapply(intrequireDay) {
super.print();
returnrequireDay<=MAX_DAY;
    }
}

HandlerClient.java

publicclassHandlerClient {
privatestaticfinalAutoHandlerAUTO_HANDLER;
static {
ManagerHandlermanagerHandler=newManagerHandler();
LeaderHandlerleaderHandler=newLeaderHandler();
leaderHandler.setSuccessor(managerHandler);
AUTO_HANDLER=newAutoHandler();
AUTO_HANDLER.setSuccessor(leaderHandler);
    }
publicstaticvoidmain(String[] args) {
AUTO_HANDLER.apply(1);
System.out.println();
AUTO_HANDLER.apply(3);
System.out.println();
AUTO_HANDLER.apply(5);
    }
}

输出:

AutoHandler process.

AutoHandler process.


LeaderHandler process.

AutoHandler process.


LeaderHandler process.

ManagerHandler process.

2.2 请求处理多次

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

场景

  • 过滤器 / 拦截器
  • JavaEE 的 Servlet 规范定义的 Filter

代码示例

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

3. 项目实践

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

{
"name": "Document no",
"code": "service_order_code",
"isBasicField": true,
"fromRerating": false,
"classType": "java.lang.String"}

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

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

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

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

首先声明抽象处理类

/*** 责任链抽象处理器*/publicabstractclassAbstractHandler<T, V> {
protectedAbstractHandler<T, V>successor;
publicvoidsetSuccessor(AbstractHandler<T, V>handler) {
this.successor=handler;
    }
/*** 处理方法** @param context 上下文* @return R*/publicabstractStringprocess(Context<T, V>context);
}

各具体处理类安排上

/*** 责任链入口,扩展字段处理*/publicclassFirstExtendFieldHandlerextendsAbstractHandler<AdjustmentTemplateDTO, String> {
/*** 处理方法** @param context 上下文* @return R*/@OverridepublicStringprocess(Context<AdjustmentTemplateDTO, String>context) {
AdjustmentTemplateDTOrequest=context.getRequest();
if (!request.getBasicField()) {
StringtmpField=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);
        }
returnsuccessor.process(context);
    }
}
/*** 非空字段处理器*/publicclassSecondNotNullFieldHandlerextendsAbstractHandler<AdjustmentTemplateDTO, String> {
/*** 处理方法** @param context 上下文* @return R*/@OverridepublicStringprocess(Context<AdjustmentTemplateDTO, String>context) {
AdjustmentTemplateDTOrequest=context.getRequest();
if (!request.getBasicField()) {
returnsuccessor.process(context);
        }
if (request.getNotNull() ==null||!request.getNotNull()) {
StringtmpField= (request.getFromRerating() ?NEW : OLD) +request.getCode();
context.setResult(tmpField);
returnsuccessor.process(context);
        } else {
StringoldValue=OLD+request.getCode();
context.setResult(oldValue);
oldValue=successor.process(context);
StringnewValue=NEW+request.getCode();
context.setResult(newValue);
newValue=successor.process(context);
StringfinalField=String.format(OdpsConstants.IF, oldValue, newValue, oldValue);
context.setResult(finalField);
returnfinalField;
        }
    }
}
/*** 时间字段处理*/publicclassThirdTimeFormatHandlerextendsAbstractHandler<AdjustmentTemplateDTO, String> {
@OverridepublicStringprocess(Context<AdjustmentTemplateDTO, String>context) {
AdjustmentTemplateDTOrequest=context.getRequest();
StringfinalSql=StringUtils.isNotBlank(request.getTimeFormat())
?String.format(TIMESTAMP_FORMAT, context.getResult(), request.getTimeFormat())
            : context.getResult();
context.setResult(finalSql);
returnfinalSql;
    }
}

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

/*** 责任链客户端*/publicclassHandlerChainClient {
privatestaticfinalFirstExtendFieldHandlerFIRST_HANDLER;
static {
ThirdTimeFormatHandlerthirdHandler=newThirdTimeFormatHandler();
SecondNotNullFieldHandlersecondHandler=newSecondNotNullFieldHandler();
secondHandler.setSuccessor(thirdHandler);
FIRST_HANDLER=newFirstExtendFieldHandler();
FIRST_HANDLER.setSuccessor(secondHandler);
    }
/*** 调用责任链进行处理** @param request 请求参数* @return result*/publicstaticStringprocess(AdjustmentTemplateDTOrequest) {
AdjustmentContextcontext=newAdjustmentContext();
context.setRequest(request);
returnFIRST_HANDLER.process(context);
    }
}

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

HandlerChainClient.process(request);

4. 优点

  • 解耦。请求发送者无需知道请求在何时、何处以及如何被处理,实现了发送者与处理者的解耦。
  • 灵活、可插拔。可以看到想要添加一个处理流程,只需实现BaseHandler,然后注入到对应的位置即可;删除一个流程也是一样,只需要将本节点的位置替换成下一个节点即可,客户端无需感知处理器的变化。
  • 代码优雅,责任链相比 if-else 是更加优雅的。

5. 缺点

  • 类的数量变多了,组链时要注意避免出现环状结构,导致出现死循环。


目录
相关文章
|
2天前
|
设计模式 编解码 C++
【ffmpeg 视频播放】深入探索:ffmpeg视频播放优化策略与设计模式的实践应用(一)
【ffmpeg 视频播放】深入探索:ffmpeg视频播放优化策略与设计模式的实践应用
63 0
|
2天前
|
设计模式 存储 缓存
【ffmpeg 视频播放】深入探索:ffmpeg视频播放优化策略与设计模式的实践应用(二)
【ffmpeg 视频播放】深入探索:ffmpeg视频播放优化策略与设计模式的实践应用
33 0
|
21小时前
|
设计模式 存储 SQL
第四篇 行为型设计模式 - 灵活定义对象间交互
第四篇 行为型设计模式 - 灵活定义对象间交互
|
2天前
|
设计模式 算法 搜索推荐
【PHP开发专栏】PHP设计模式解析与实践
【4月更文挑战第29天】本文介绍了设计模式在PHP开发中的应用,包括创建型(如单例、工厂模式)、结构型和行为型模式(如观察者、策略模式)。通过示例展示了如何在PHP中实现这些模式,强调了它们在提升代码可维护性和可扩展性方面的作用。设计模式是解决常见问题的最佳实践,但在使用时需避免过度设计,根据实际需求选择合适的设计模式。
|
2天前
|
设计模式 Java
Java 设计模式:混合、装饰器与组合的编程实践
【4月更文挑战第27天】在面向对象编程中,混合(Mixins)、装饰器(Decorators)和组合(Composition)是三种强大的设计模式,用于增强和扩展类的功能。
21 1
|
2天前
|
设计模式 算法 Java
[设计模式Java实现附plantuml源码~行为型]定义算法的框架——模板方法模式
[设计模式Java实现附plantuml源码~行为型]定义算法的框架——模板方法模式
|
2天前
|
设计模式 JavaScript Java
[设计模式Java实现附plantuml源码~行为型] 对象状态及其转换——状态模式
[设计模式Java实现附plantuml源码~行为型] 对象状态及其转换——状态模式
|
2天前
|
设计模式 Go
[设计模式 Go实现] 行为型~解释器模式
[设计模式 Go实现] 行为型~解释器模式
|
2天前
|
设计模式 Go
[设计模式 Go实现] 行为型~迭代器模式
[设计模式 Go实现] 行为型~迭代器模式
|
2天前
|
设计模式 存储 Go
[设计模式 Go实现] 行为型~备忘录模式
[设计模式 Go实现] 行为型~备忘录模式