概念

职责链模式(Chain of Responsibility Pattern)是一种行为型设计模式,它允许多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系


职责链模式通常涉及两个核心角色:处理者(Handler)和客户端(Client)

  • 处理者(Handler)

    定义了一个处理请求的接口,并维护了一个后继处理者的引用

    处理者可以决定是否处理请求,或者将请求传递给后继处理者

  • 客户端(Client)

    创建处理者对象,并将请求发送给处理者链的第一个处理者

    客户端通常不需要知道处理者链的具体结构,只需要将请求发送给第一个处理者即可


职责链模式的核心思想是将请求发送者和接收者解耦,使得多个对象都有机会处理请求,并且可以灵活地调整处理者链的结构

这种模式的优点在于,可以降低请求发送者和接收者之间的耦合度,提高系统的灵活性和可扩展性

职责链模式适用于以下情况:

  • 当有多个对象可以处理同一个请求,并且希望将请求发送者和接收者解耦时,可以使用职责链模式
  • 当希望动态地调整处理者链的结构,并且希望在运行时决定请求的处理者时,职责链模式也是一个很好的选择


举个简单的例子,考虑一个请假审批系统

请假申请可以被多个领导依次审批,而每个领导都有不同的审批权限

职责链模式可以将每个领导视为一个处理者对象,并将请假申请发送给处理者链的第一个处理者

如果第一个处理者无法处理请求,则将请求传递给下一个处理者,直到找到能够处理请求的处理者为止

这样,可以实现请求发送者和接收者的解耦,同时也提供了一种灵活的方式来处理请求


实现条件

  1. 多个对象处理同一请求

    职责链模式适用于多个对象处理同一请求的情况,请求会按照一定的顺序经过一系列的处理者,直到找到合适的处理者处理请求为止

  2. 请求发送者不需要明确知道接收

  3. 职责链模式适用于请求发送者不需要明确知道接收者是谁的情况,请求发送者只需要将请求发送给第一个处理者,由处理者之间相互传递请求,直到找到合适的处理者

  4. 请求可以被任意处理者处理

    职责链模式适用于请求可以被任意处理者处理的情况,处理者之间没有严格的顺序要求,可以根据具体情况自由选择处理请求的方式

  5. 需要动态添加或者删除处理者

    职责链模式适用于需要动态添加或者删除处理者的情况,处理者之间的关系是动态变化的,可以根据需要灵活地调整处理者的顺序和数量


优点

  1. 降低耦合度

    职责链模式将请求发送者和接收者解耦,请求发送者不需要知道具体的接收者是谁,接收者也不需要知道请求的发送者是谁,从而降低了对象之间的耦合度

  2. 增强灵活性

    职责链模式允许请求沿着一条链传递,每个处理者都有机会处理请求或者将请求传递给下一个处理者,从而增强了系统的灵活性和可扩展性

  3. 简化对象的相互连接

    职责链模式可以简化对象之间的相互连接,每个处理者只需要保存对下一个处理者的引用即可,无需了解整个处理链的结构,降低了系统的复杂度

  4. 增强了请求的处理过程

    职责链模式将请求的处理过程分解成多个处理者来共同完成,每个处理者只负责自己能够处理的部分,使得请求的处理过程更加清晰和可控

  5. 动态添加或者删除处理者

    责链模式允许动态地添加或者删除处理者,处理者之间的关系是动态变化的,可以根据需要灵活地调整处理者的顺序和数量


缺点

  1. 请求可能未被处理

    如果请求没有合适的处理者来处理,可能会导致请求未被处理,需要额外的机制来处理这种情况,例如设置一个默认处理者

  2. 性能问题

    职责链模式可能会导致性能问题,特别是处理链比较长或者请求频繁的情况下,可能会造成请求的传递和处理延迟

  3. 调试困难

    由于请求的处理过程被分解到多个处理者中,可能会导致调试困难,难以确定请求的处理流程和具体是哪个处理者处理了请求

  4. 可能导致循环引用

    如果处理链中存在循环引用的情况,可能会导致请求无法正常处理,甚至造成系统崩溃,需要谨慎设计处理链的结构


实现方式

处理者基类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/**
* Processor类定义了一个处理器的基本结构,包括处理器的名称、权限级别以及下一个处理器的设置。
* 它是一个抽象类,不允许直接实例化,必须通过继承来扩展。
*/
class Processor {
/**
* 构造函数初始化处理器的名称和权限级别,并检查是否尝试直接实例化抽象类。
* @param {string} name 处理器的名称。
* @param {number} authorityLevel 处理器的权限级别。
* @throws {Error} 如果尝试直接实例化Processor类,则抛出错误。
*/
constructor(name, authorityLevel) {
if (new.target === Processor) {
throw new Error("不能实例化抽象类Processor")
}
this.name = name
this.authorityLevel = authorityLevel
this.nextProcessor = null
}

/**
* 设置下一个处理器。
* @param {Processor} nextProcessor 下一个处理器的实例。
* @throws {Error} 如果传入的不是Processor的实例,则抛出错误。
*/
setNextProcessor(nextProcessor) {
if (!(nextProcessor instanceof Processor)) {
throw new Error("下一个审批者必须是Processor的实例")
}
this.nextProcessor = nextProcessor
}

/**
* 处理请求的方法,这是一个抽象方法,必须在子类中实现。
* @param {Object} request 待处理的请求对象。
* @throws {Error} 必须在子类中实现此方法,否则抛出错误。
*/
processRequest(request) {
throw new Error("必须实现processRequest方法")
}
}

export default Processor


子类基类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
// 导入链式责任模式的处理器基类
import Processor from '../ChainOfResponsibilityPattern'

/**
* 团队领导类,继承自处理器基类,处理员工请假请求。
*/
class TeamLeader extends Processor {
/**
* 构造函数,初始化团队领导实例。
*/
constructor() {
super("团队领导", 2) // 调用父类构造器,设置名称为“团队领导”,审批权限级别为2
}

/**
* 处理请假请求的方法。
* @param {Object} request 请假请求对象,包含请假天数等信息。
* @returns
*/
processRequest(request) {
// 如果请假天数在团队领导的审批权限范围内,则审批通过
if (request.leaveDays <= this.authorityLevel) {
console.log(`${this.name}审批了${request.leaveDays}天的请假。`)
} else if (this.nextProcessor) {
// 如果请假天数超出团队领导的审批范围,但存在下一个处理器(比如经理),则转交下一个处理器处理
console.log(`${this.name}无法处理,请转交给${this.nextProcessor.name}`)
this.nextProcessor.processRequest(request)
} else {
// 如果请假天数超出团队领导的审批范围,并且不存在下一个处理器,则审批不通过
console.log(`无法处理,请假天数过长。`)
}
}
}

// 导出TeamLeader类
export default TeamLeader



// 导入链式责任模式的处理器基类
import Processor from '../ChainOfResponsibilityPattern'

/**
* 部门经理类,继承自处理器基类,用于处理员工请假请求。
*/
class DepartmentManager extends Processor {
/**
* 构造函数初始化部门经理。
*/
constructor() {
super("部门经理", 5) // 调用父类构造函数,设置名称为“部门经理”,审批权限级别为5天
}

/**
* 处理请假请求。
* @param {Object} request 请假请求对象,包含请假天数等信息。
*/
processRequest(request) {
// 如果请假天数在部门经理的审批权限范围内,则审批通过
if (request.leaveDays <= this.authorityLevel) {
console.log(`${this.name}审批了${request.leaveDays}天的请假。`)
} else if (this.nextProcessor) { // 如果存在下一个处理器(上级领导),则将请求转交
console.log(`${this.name}无法处理,请转交给${this.nextProcessor.name}`)
this.nextProcessor.processRequest(request)
} else { // 如果没有下一个处理器,表示无法处理此请求
console.log(`无法处理,请假天数过长。`)
}
}
}

// 导出部门经理类
export default DepartmentManager


// 导入链式责任模式的处理器基类
import Processor from '../ChainOfResponsibilityPattern'

/**
* 人力资源经理类,继承自处理器基类,用于处理员工请假请求。
*/
class HRManager extends Processor {
/**
* 构造函数初始化人力资源经理。
*/
constructor() {
super("人力资源经理", 10) // 调用父类构造函数,设置名称为“人力资源经理”,审批权限级别为10天
}

/**
* 处理请假请求。
* @param {Object} request 请假请求对象,包含请假天数等信息。
*/
processRequest(request) {
// 如果请假天数在权限级别内,审批通过
if (request.leaveDays <= this.authorityLevel) {
console.log(`${this.name}审批了${request.leaveDays}天的请假。`)
} else if (this.nextProcessor) { // 如果存在下一个处理器,则将请求转交给下一个处理器
console.log(`${this.name}无法处理,请转交给${this.nextProcessor.name}`)
this.nextProcessor.processRequest(request)
} else { // 如果没有下一个处理器,则表示无法处理该请求
console.log(`无法处理,请假天数过长。`)
}
}
}

// 导出HRManager类
export default HRManager


/**
* LeaveRequest 类用于创建一个请假申请实例。
* @param {number} leaveDays 请假天数。
*/
class LeaveRequest {
constructor(leaveDays) {
this.leaveDays = leaveDays; // 初始化请假天数
}
}

export default LeaveRequest; // 导出LeaveRequest类作为默认模块


怎么使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/**
* 主程序入口用于演示如何处理员工请假流程。
* 首先,创建了团队领导、部门经理和人力资源经理的实例。
* 然后,设定它们之间的处理顺序,即团队领导先处理请假申请,然后传递给部门经理,最后由人力资源经理处理。
* 接着,创建了几个请假申请实例,并通过团队领导的实例处理这些请假申请。
*/

// 导入相关的类
import TeamLeader from '../SubClass/TeamLeader'
import DepartmentManager from '../SubClass/DepartmentManager'
import HRManager from '../SubClass/HRManager'
import LeaveRequest from '../SubClass/LeaveRequest'

// 创建团队领导、部门经理和人力资源经理的实例
const teamLeader = new TeamLeader()
const departmentManager = new DepartmentManager()
const hrManager = new HRManager()

// 设置处理流程的链式关系
teamLeader.setNextProcessor(departmentManager)
departmentManager.setNextProcessor(hrManager)

// 创建请假申请实例,并提交给团队领导处理
const request1 = new LeaveRequest(1)
const request3 = new LeaveRequest(3)
const request7 = new LeaveRequest(7)
const request12 = new LeaveRequest(12)

teamLeader.processRequest(request1)
teamLeader.processRequest(request3)
teamLeader.processRequest(request7)
teamLeader.processRequest(request12)


场景

  1. 电子商务订单处理系统

    假设一个电子商务平台,订单的处理包括库存检查、支付验证、地址验证、配送等多个环节

    每个环节都有不同的处理者,可以使用职责链模式来构建订单处理流程

    订单首先经过库存检查处理者,如果库存充足,则传递给支付验证处理者,依次类推,直到订单被完全处理

  2. 工作流引擎

    工作流引擎是一种用于管理和执行工作流程的系统

    在工作流引擎中,每个工作流程包含多个步骤或任务,每个步骤都有对应的处理者

    职责链模式可以用来管理工作流程中的步骤和处理者,以实现任务的自动化处理

  3. 网络安全防护系统

    在网络安全防护系统中,通常会对网络流量进行多层次的检测和过滤,包括入侵检测、恶意软件检测、数据包过滤等

    每个检测和过滤功能可以作为一个处理者,通过职责链模式将它们串联起来,以便逐层检测和过滤网络流量

  4. 工程质量检查系统

    在软件开发中,经常需要进行代码审查和质量检查,以确保代码符合规范和质量标准

    可以使用职责链模式来构建质量检查系统,其中每个质量检查功能(如代码格式检查、代码风格检查、代码性能检查等)作为一个处理者,根据需要逐个应用于待检查的代码

  5. 文件处理系统

    在文件处理系统中,可能需要对不同类型的文件进行处理,如文本文件、图像文件、音频文件等

    每种类型的文件都需要不同的处理方式,可以使用职责链模式来管理文件处理流程,使得每个文件根据其类型被传递给相应的处理者进行处理


源代码