通过使用责任链模式,我们可以更加灵活和优雅地处理请求,降低代码之间的耦合度,提高代码的可维护性和可扩展性。在一些具有复杂业务逻辑或需要动态处理请求的场景下,使用责任链模式将是一个很好的选择。本文将通过一个具体的示例来详细介绍如何在业务代码中优雅地使用责任链模式。
引言
在软件开发中,我们经常会遇到一种情况,即一个请求需要经过多个处理节点才能得到最终结果。这种情况下,我们可以使用责任链模式来优雅地处理请求。责任链模式是一种行为设计模式,它可以创建一条由多个处理节点组成的链,每个节点都有机会处理请求,也可以决定将请求传递给下一个节点。这种模式可以将请求的发送者和接收者解耦,从而提高代码的灵活性和可维护性。
责任链模式的使用场景通常包括以下几种情况:
有多个对象可以处理请求,但具体由哪个对象处理请求在运行时才能确定。
想要在不明确指定接收者的情况下,向多个对象中的一个提交请求。
希望动态指定处理请求的对象集合。
责任链模式的定义
▐ 定义
使多个对象都有机会处理请求,从而避免请求的发送者与请求处理者耦合在一起。将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。
▐ 类型
对象行为型模式
▐ 实质
责任链上的处理者负责处理请求,客户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,从而实现请求发送者与请求处理者的解耦。
优雅之处
使用责任链模式的好处和目标主要有以下几点:
解耦责任链的节点:责任链模式可以将请求的发送者和接收者解耦,使得它们不需要直接相互引用,从而降低了对象之间的耦合度。
提高代码的灵活性和可维护性:责任链模式可以在运行时动态地改变处理节点的顺序和数量,从而灵活地处理不同的请求,同时也方便了代码的维护和扩展。
增加新的处理节点方便:由于责任链模式将处理节点解耦,因此可以方便地增加新的处理节点到责任链中,而不需要修改已有的代码。
动态处理请求:责任链模式允许在运行时根据具体的情况动态地组合和排序处理者,从而实现灵活的请求处理逻辑。
通过使用责任链模式,我们可以更加灵活和优雅地处理请求,降低代码之间的耦合度,提高代码的可维护性和可扩展性。在一些具有复杂业务逻辑或需要动态处理请求的场景下,使用责任链模式将是一个很好的选择。接下来,我们将通过一个具体的示例来详细介绍如何在业务代码中优雅地使用责任链模式。
责任链模式适合应用场景
当程序需要使用不同方式处理不同种类请求, 而且请求类型和顺序预先未知时, 可以使用责任链模式。
该模式能将多个处理者连接成一条链。接收到请求后, 它会 “询问” 每个处理者是否能够对其进行处理。这样所有处理者都有机会来处理请求。
当必须按顺序执行多个处理者时, 可以使用该模式。
无论你以何种顺序将处理者连接成一条链, 所有请求都会严格按照顺序通过链上的处理者。
如果所需处理者及其顺序必须在运行时进行改变, 可以使用责任链模式。
如果在处理者类中有对引用成员变量的设定方法, 你将能动态地插入和移除处理者, 或者改变其顺序。
▐ 实现方式
声明处理者接口并描述请求处理方法的签名。
确定客户端如何将请求数据传递给方法。最灵活的方式是将请求转换为对象, 然后将其以参数的形式传递给处理函数。
为了在具体处理者中消除重复的样本代码, 你可以根据处理者接口创建抽象处理者基类。
该类需要有一个成员变量来存储指向链上下个处理者的引用。你可以将其设置为不可变类。但如果你打算在运行时对链进行改变, 则需要定义一个设定方法来修改引用成员变量的值。
为了使用方便, 你还可以实现处理方法的默认行为。如果还有剩余对象, 该方法会将请求传递给下个对象。具体处理者还能够通过调用父对象的方法来使用这一行为。
依次创建具体处理者子类并实现其处理方法。每个处理者在接收到请求后都必须做出两个决定:
是否自行处理这个请求。
是否将该请求沿着链进行传递。
客户端可以自行组装链, 或者从其他对象处获得预先组装好的链。在后一种情况下, 你必须实现工厂类以根据配置或环境设置来创建链。
客户端可以触发链中的任意处理者, 而不仅仅是第一个。请求将通过链进行传递, 直至某个处理者拒绝继续传递, 或者请求到达链尾。
由于链的动态性, 客户端需要准备好处理以下情况:
-
链中可能只有单个链接。
部分请求可能无法到达链尾。
其他请求可能直到链尾都未被处理。
应用示例
我们拿请假的场景来说明,我们请假时间分为3天,5天,7天等,对应请假天数审批人不同,可能请假3天由你的项目小组长审批即可,但是请假5天可能需要由部门总来审批。在这个场景中就十分适合使用责任链模式进行构建。当然也可以用大量的if-else来处理,但是相对于if-else来讲责任链模式由些好处:
责任链模式与 if...else 相比,他的耦合性要低一些,因为它将条件判定分散到各个处理类中,并且这些处理类的优先处理顺序可以随意的设定,并且如果想要添加新的 handler 类也是十分简单的,这符合开放闭合原则。
责任链模式带来了灵活性,但是在设置处理类前后关系时,一定要避免在链中出现循环引用的问题。
实现步骤
▐ 循环责任链模式步骤
使用 for 循环来处理责任链可以更加直观地解释责任链模式的执行过程。
假设我们有一个责任链,包含了多个处理器 Processor1、Processor2、Processor3。每个处理器都可以处理请求,如果一个处理器能够处理请求,就处理并结束责任链的执行;如果处理器不能处理请求,就将请求传递给下一个处理器。
我们可以使用一个 for 循环来遍历责任链中的处理器,并依次进行处理。当遇到第一个能够处理请求的处理器时,就处理请求并结束循环。
Request request = new Request(); // 创建请求对象
Processor[] processors = {new Processor1(), new Processor2(), new Processor3()};
for (Processor processor : processors) {
if (processor.canHandle(request)) {
processor.process(request);
break; // 处理请求并结束循环
}
}
在上述代码中,我们依次遍历责任链中的处理器,通过调用 canHandle 方法判断是否能够处理请求。如果能够处理,则调用 process 方法进行处理,并使用 break 语句结束循环。如果所有处理器都不能处理请求,循环结束后可以进行相应的处理逻辑。
使用 for 循环来处理责任链能够更加清晰地表达责任链模式的执行过程,我们按照顺序遍历处理器,并根据需要进行处理。如果有多个处理器都能处理请求,我们可以通过调整处理器的顺序来确定处理的优先级。同时,我们也可以在循环结束后根据实际需求进行相应的处理,比如报错或给出默认处理等。
责任链模式的设计思想就是将一个请求沿着一条链传递,每个节点都有机会处理请求或者将其传递给下一个节点。这种设计类似于流水线上的工作流程,每个节点都负责处理某个环节的工作,并将结果传递给下一个节点。使用流程图可以很好地表示责任链模式的执行流程。每个节点可以表示为一个流程图中的步骤,箭头表示请求的传递方向。整个流程图可以清晰地展示责任链模式中各个节点的工作顺序和流程。
通过流程图的抽象表示,我们可以更好地理解责任链模式的执行流程,便于沟通和交流,也有助于设计和实现责任链模式。同时,流程图也可以帮助开发人员更好地理解和维护责任链模式的代码逻辑。
▐ 请假场景步骤
请假场景中的责任链模式可以通过实现一个LeaveRequestProcessor接口来实现,每个具体处理者都实现该接口,根据请假天数来确定是否能够处理请求,以及是否将请求传递给下一个处理者。
使用 for 循环来实现责任链模式可以更加直观地展示责任链的执行过程:
首先,定义LeaveRequestProcessor接口:
public interface LeaveRequestProcessor {
void processLeaveRequest(LeaveRequest request);
}
TeamLeaderProcessor(项目小组长处理器):
public class TeamLeaderProcessor implements LeaveRequestProcessor {
public void processLeaveRequest(LeaveRequest request) {
if (request.getDays() <= 3) {
// 处理请假请求
System.out.println("项目小组长批准了请假申请,天数为:" + request.getDays() + "天");
} else {
System.out.println("项目小组长无法处理该请假申请");
}
}
}
DepartmentManagerProcessor(部门经理处理器):
public class DepartmentManagerProcessor implements LeaveRequestProcessor {
public void processLeaveRequest(LeaveRequest request) {
if (request.getDays() > 3 && request.getDays() <= 5) {
// 处理请假请求
System.out.println("部门经理批准了请假申请,天数为:" + request.getDays() + "天");
} else {
System.out.println("部门经理无法处理该请假申请");
}
}
}
CEOProcessor(总经理处理器):
public class CEOProcessor implements LeaveRequestProcessor {
public void processLeaveRequest(LeaveRequest request) {
if (request.getDays() > 5 && request.getDays() <= 7) {
// 处理请假请求
System.out.println("总经理批准了请假申请,天数为:" + request.getDays() + "天");
} else {
System.out.println("总经理无法处理该请假申请");
}
}
}
然后,创建一个处理器列表,并使用 for 循环遍历处理器列表来处理请假请求:
List<LeaveRequestProcessor> processors = new ArrayList<>();
processors.add(new TeamLeaderProcessor());
processors.add(new DepartmentManagerProcessor());
processors.add(new CEOProcessor());
// 创建请假请求
LeaveRequest request = new LeaveRequest("John", 5);
for (LeaveRequestProcessor processor : processors) {
processor.processLeaveRequest(request);
}
在上述代码中,我们创建了一个处理器列表,并依次添加了项目小组长(TeamLeaderProcessor)、部门经理(DepartmentManagerProcessor)和总经理(CEOProcessor)这三个具体处理者。然后,我们使用 for 循环遍历处理器列表,并依次调用每个处理器的 processLeaveRequest 方法来处理请假请求。
每个处理器在处理请求时,根据请假天数来判断是否能够处理请求。如果能够处理请求,则进行处理并结束循环;如果不能处理请求,则继续下一个处理器。这样,请求会依次在处理器列表中传递,直到找到能够处理请求的处理器为止。
通过使用 for 循环来实现责任链模式,我们更加直观地表达了责任链模式的执行过程。同时,通过调整处理器在处理器列表中的顺序,可以灵活地改变处理请求的优先级。在业务逻辑发生变化时,我们只需要新增或修改相应的处理器即可,而不需要修改已有的处理器或请求发送者的代码,提高了代码的可维护性和可扩展性。
这样,通过责任链模式,我们可以灵活地处理请假请求,并且根据请假天数确定由哪个处理者来审批。在业务逻辑发生变化时,我们只需要新增或修改相应的处理者即可,而不需要改动已有的处理者或请求发送者的代码,提高了代码的可维护性和可扩展性。
参考资料
https://juejin.cn/post/7273028474981335081责任链的使用
https://refactoringguru.cn/design-patterns/chain-of-responsibility责任链模式
团队介绍
淘天集团-内容消费&社区互动团队,是阿里的一支明星团队,目前负责阿里电商平台的首页和信息流推荐,其中手机淘宝首页、逛逛、信息流、购后链路等场景每天服务数亿用户,大促核心系统峰值QPS千万级,工作涉及全链路端到端性能优化,流量效率提升、用户体验、提高商家及达人参与淘宝的积极性,优化商业生态运行机制。在过去的几年时间,一直与业界领先的算法团队紧密协作,不断拓展业务边界并将核心业务指标一次次踩在脚下。我们专注手机淘宝首页、淘宝逛逛、推荐信息流核心链路业务支持和业务平台抽象。这里有巨大的流量,可以满足你对高并发大规模分布式系统练手的畅想;这里有最前沿的算法应用场景,可以玩转各种智能创新;这里有最严苛的系统指标要求,可以让你感受到优化复杂系统的快感~
目前正在招聘感兴趣的同学(校招社招均有),欢迎交流沟通或投递简历 linzesi.lzs@taobao.com。
¤ 拓展阅读 ¤
3DXR技术 | 终端技术 | 音视频技术
服务端技术 | 技术质量 | 数据算法