一、引言
在软件工程,软件设计与开发过程中,我们常常会遇到这样的情况:一个请求需要被多个对象处理,但请求的发送者并不知道到底哪一个对象最终会处理这个请求。这时候,职责链模式就派上用场了。职责链模式能够将请求的发送者和接收者解耦,让多个对象都有机会处理请求,形成一个链状结构,请求在这个链上传递,直到有对象处理它为止。
二、定义与描述
职责链模式(Chain of Responsibility Pattern)是一种行为型设计模式,它将请求的发送者和接收者解耦,使多个对象都有机会处理这个请求。在职责链模式中,通常会创建一个由多个处理对象组成的链,每个处理对象都包含对下一个处理对象的引用。当一个请求被发送到链中的第一个处理对象时,这个对象会先检查自己是否能够处理该请求,如果可以则处理,否则将请求转发给链中的下一个处理对象,依此类推,直到请求被处理或者到达链的末尾。
三、抽象背景
假设我们正在开发一个员工请假审批系统。员工提出请假申请后,需要根据请假天数经过不同级别的审批。例如,请假1 - 3天由组长审批,3 - 7天由部门经理审批,7天以上由总经理审批。如果没有职责链模式,我们可能会在代码中使用大量的if - else语句来判断应该由谁审批,这样会导致代码的可维护性和扩展性较差。使用职责链模式,我们可以将不同级别的审批者看作是链上的不同节点,请假申请在这个链上传递,直到被合适的审批者处理。
四、适用场景与现实问题解决
- 事件处理系统:在图形用户界面(GUI)中,多个事件处理程序可能对一个事件感兴趣。例如,鼠标点击事件可能先被窗口组件接收,然后是面板组件,最后是按钮组件。如果按钮组件能够处理该事件(例如触发一个点击动作),则处理该事件,否则事件继续向上传递。
- 日志记录系统:不同级别的日志(DEBUG、INFO、WARN、ERROR)可以通过职责链进行处理。例如,DEBUG级别的日志可能只在开发环境中由专门的DEBUG日志处理器处理,而ERROR级别的日志则可能需要在多个处理器(如本地日志文件、远程日志服务器等)中依次处理。
事件处理系统
事件类型 | 接收组件顺序 | 组件处理能力 | 处理结果 |
---|---|---|---|
鼠标点击事件 | 窗口组件 -> 面板组件 -> 按钮组件 | 窗口组件:可能无直接处理逻辑,仅传递 面板组件:可能无直接处理逻辑,仅传递 按钮组件:能触发点击动作 | 若按钮组件能处理,执行点击动作;否则事件继续向上传递 |
日志记录系统
日志级 别 | 可能的处理器顺序 | 处理器处理逻辑 | 处理结果 |
---|---|---|---|
DEBUG | 专门的 DEBUG 日志处理器 | 仅在开发环境中处理 DEBUG 级别的日志 | 在开发环境处理,非开发环境忽略 |
INFO | 可设置 INFO 日志处理器(如控制台输出) | 输出 INFO 级别的日志信息 | 输出日志 |
WARN | 可设置 WARN 日志处理器(如本地文件存储) | 存储 WARN 级别的日志信息 | 记录日志到本地文件 |
ERROR | 本地日志文件处理器 -> 远程日志服务器处理器 | 本地日志文件处理器:将 ERROR 日志记录到本地文件 远程日志服务器处理器:将 ERROR 日志发送到远程服务器 | 依次在本地文件和远程服务器记录日志 |
五、职责链模式现实生活例子
- 医院看病流程:患者去医院看病,先到挂号处挂号,然后到门诊医生处就诊,如果医生认为需要做检查,患者就会到检查科室进行检查,检查结果出来后再回到门诊医生处,医生根据检查结果做出诊断,如果病情严重可能会转到专家门诊或者住院部。这个过程就像是一个职责链,每个环节(挂号处、门诊医生、检查科室、专家门诊、住院部等)都有自己的职责,患者在这个链上流动,直到病情得到合适的处理。
- 邮件投递系统:一封邮件从发件人发出后,会经过多个邮件服务器的转发,每个邮件服务器都会检查邮件的目标地址是否在自己的服务范围内,如果是则直接投递,如果不是则将邮件转发到下一个可能的邮件服务器,直到邮件到达目的地。
六、初衷与问题解决
初衷是为了将请求的发送者和接收者解耦,避免请求发送者与多个潜在接收者之间的复杂耦合关系。通过将处理逻辑分散到一个链上的多个对象中,使得代码更易于维护和扩展。如果需要添加新的处理者或者修改处理逻辑,只需要在链上添加或修改相应的处理对象即可,而不需要修改请求的发送者代码。
七、代码示例
Java代码示例
java
// 抽象处理者
abstract class Handler {
protected Handler successor;
public void setSuccessor(Handler successor) {
this.successor = successor;
}
abstract void handleRequest(int request);
}
// 具体处理者1
class ConcreteHandler1 extends Handler {
@Override
void handleRequest(int request) {
if (request >= 1 && request <= 3) {
System.out.println("ConcreteHandler1处理请求: " + request);
} else if (successor!= null) {
successor.handleRequest(request);
}
}
}
// 具体处理者2
class ConcreteHandler2 extends Handler {
@Override
void handleRequest(int request) {
if (request > 3 && request <= 7) {
System.out.println("ConcreteHandler2处理请求: " + request);
} else if (successor!= null) {
successor.handleRequest(request);
}
}
}
// 具体处理者3
class ConcreteHandler3 extends Handler {
@Override
void handleRequest(int request) {
if (request > 7) {
System.out.println("ConcreteHandler3处理请求: " + request);
} else if (successor!= null) {
successor.handleRequest(request);
}
}
}
public class ChainOfResponsibilityJava {
public static void main(String[] args) {
Handler handler1 = new ConcreteHandler1();
Handler handler2 = new ConcreteHandler2();
Handler handler3 = new ConcreteHandler3();
handler1.setSuccessor(handler2);
handler2.setSuccessor(handler3);
int[] requests = {1, 5, 10};
for (int request : requests) {
handler1.handleRequest(request);
}
}
}
类图:
类图解释
Handler
是抽象处理者类,包含一个successor
引用和抽象方法handleRequest
以及设置后继者的方法setSuccessor
。ConcreteHandler1
、ConcreteHandler2
和ConcreteHandler3
是具体处理者类,继承自Handler
并实现了handleRequest
方法。ChainOfResponsibilityJava
是主类,它创建并使用Handler
对象。Handler
类通过successor
关联自身,形成职责链。
流程图:
流程图解释
- 首先创建具体处理者对象并设置职责链。
- 定义请求数组,依次取出请求并发送给链的第一个处理者
handler1
。 - 每个处理者检查自己是否能处理请求,如果能则处理,不能则传递给后继者,直到请求被处理或链结束。
时序图:
时序图解释
Client
启动ChainOfResponsibilityJava
程序。ChainOfResponsibilityJava
创建具体处理者对象并设置职责链。- 对于每个请求,将其发送给
Handler1
,根据请求的范围,可能由Handler1
、Handler2
或Handler3
处理。每个处理者被激活处理请求,处理完成后被停用。
C++代码示例
cpp
#include <iostream>
// 抽象处理者
class Handler {
public:
virtual ~Handler() {}
virtual void handleRequest(int request) = 0;
void setSuccessor(Handler* successor) {
this->successor = successor;
}
protected:
Handler* successor;
};
// 具体处理者1
class ConcreteHandler1 : public Handler {
public:
void handleRequest(int request) override {
if (request >= 1 && request <= 3) {
std::cout << "ConcreteHandler1处理请求: " << request << std::endl;
} else if (successor!= nullptr) {
successor->handleRequest(request);
}
}
};
// 具体处理者2
class ConcreteHandler2 : public Handler {
public:
void handleRequest(int request) override {
if (request > 3 && request <= 7) {
std::cout << "ConcreteHandler2处理请求: " << request << std::endl;
} else if (successor!= nullptr) {
successor->handleRequest(request);
}
}
};
// 具体处理者3
class ConcreteHandler3 : public Handler {
public:
void handleRequest(int request) override {
if (request > 7) {
std::cout << "ConcreteHandler3处理请求: " << request << std::endl;
} else if (successor!= nullptr) {
successor->handleRequest(request);
}
}
};
int main() {
ConcreteHandler1* handler1 = new ConcreteHandler1();
ConcreteHandler2* handler2 = new ConcreteHandler2();
ConcreteHandler3* handler3 = new ConcreteHandler3();
handler1->setSuccessor(handler2);
handler2->setSuccessor(handler3);
int requests[] = {1, 5, 10};
for (int request : requests) {
handler1->handleRequest(request);
}
delete handler1;
delete handler2;
delete handler3;
return 0;
}
Python代码示例
python
# 抽象处理者
class Handler:
def __init__(self):
self.successor = None
def set_successor(self, successor):
self.successor = successor
def handle_request(self, request):
pass
# 具体处理者1
class ConcreteHandler1(Handler):
def handle_request(self, request):
if 1 <= request <= 3:
print(f"ConcreteHandler1处理请求: {request}")
elif self.successor is not None:
self.successor.handle_request(request)
# 具体处理者2
class ConcreteHandler2(Handler):
def handle_request(self, request):
if 3 < request <= 7:
print(f"ConcreteHandler2处理请求: {request}")
elif self.successor is not None:
self.successor.handle_request(request)
# 具体处理者3
class ConcreteHandler3(Handler):
def handle_request(self, request):
if request > 7:
print(f"ConcreteHandler3处理请求: {request}")
elif self.successor is not None:
self.successor.handle_request(request)
if __name__ == "__main__":
handler1 = ConcreteHandler1()
handler2 = ConcreteHandler2()
handler3 = ConcreteHandler3()
handler1.set_successor(handler2)
handler2.set_successor(handler3)
requests = [1, 5, 10]
for request in requests:
handler1.handle_request(request)
Go代码示例
Go
package main
import "fmt"
// 抽象处理者
type Handler interface {
setSuccessor(Handler)
handleRequest(int)
}
// 具体处理者结构体
type ConcreteHandler struct {
successor Handler
}
func (ch *ConcreteHandler) setSuccessor(successor Handler) {
ch.successor = successor
}
// 具体处理者1
type ConcreteHandler1 struct {
ConcreteHandler
}
func (ch1 *ConcreteHandler1) handleRequest(request int) {
if request >= 1 && request <= 3 {
fmt.Printf("ConcreteHandler1处理请求: %d\n", request)
} else if ch1.successor!= nil {
ch1.successor.handleRequest(request)
}
}
// 具体处理者2
type ConcreteHandler2 struct {
ConcreteHandler
}
func (ch2 *ConcreteHandler2) handleRequest(request int) {
if request > 3 && request <= 7 {
fmt.Printf("ConcreteHandler2处理请求: %d\n", request)
} else if ch2.successor!= nil {
ch2.successor.handleRequest(request)
}
}
// 具体处理者3
type ConcreteHandler3 struct {
ConcreteHandler
}
func (ch3 *ConcreteHandler3) handleRequest(request int) {
if request > 7 {
fmt.Printf("ConcreteHandler3处理请求: %d\n", request)
} else if ch3.successor!= nil {
ch3.successor.handleRequest(request)
}
}
func main() {
handler1 := &ConcreteHandler1{}
handler2 := &ConcreteHandler2{}
handler3 := &ConcreteHandler3{}
handler1.setSuccessor(handler2)
handler2.setSuccessor(handler3)
requests := []int{1, 5, 10}
for _, request := range requests {
handler1.handleRequest(request)
}
}
八、职责链模式的优缺点
优点
- 解耦请求发送者和接收者:请求的发送者不需要知道哪个具体的对象会处理请求,降低了两者之间的耦合度。
- 增强了灵活性和可扩展性:可以方便地在链中添加、删除或重新排列处理者,而不需要修改请求发送者的代码。
- 符合开闭原则:当需要添加新的处理逻辑时,只需要创建新的处理者类并将其加入到链中即可,不需要修改现有代码。
缺点
- 可能导致处理链过长:如果处理链过长,会影响性能,并且可能导致调试困难,因为请求在链上的传递过程可能比较复杂。
- 部分请求可能未被处理:如果没有合适的处理者在链上,请求可能会到达链的末尾而未被处理,需要额外的机制来处理这种情况(例如设置一个默认的处理者)。
九、职责链模式的升级版
- 责任链模式与命令模式结合:将请求封装成命令对象,然后在职责链中传递命令对象。这样可以进一步解耦请求的发送者和处理者,并且可以方便地对请求进行管理和操作(如撤销、重做等)。
- 使用动态职责链:在运行时根据不同的条件动态构建职责链,而不是在初始化时固定职责链的结构。例如,可以根据用户的权限或者系统的运行状态来动态决定哪些处理者应该在链上以及它们的顺序。