责任链模式:把请求像接力棒一样传下去
说实话,责任链模式是我在实际项目中用得最频繁的模式之一。它解决了一个很实际的问题:一个请求来了,到底该由谁处理?
你想想看,审批流程、日志过滤、异常处理,这些场景都有一个共同点——处理者不止一个,而且处理顺序很重要。责任链模式,说白了就是把多个处理者串成一条链,请求沿着链传递,直到有人处理它。
模式的核心思想
责任链模式的定义其实很简单:避免请求发送者与接收者耦合,让多个对象都有机会处理请求。将这些对象连成一条链,沿着链传递请求,直到有对象处理它为止。
我个人习惯把责任链想象成「击鼓传花」——花就是请求,每个人都可以选择处理它,或者传给下一个人。只不过在代码里,这个传递是有明确规则的。
核心角色:
- 抽象处理者(Handler):定义处理请求的接口,持有下一个处理者的引用
- 具体处理者(ConcreteHandler):实现处理逻辑,决定是否处理或转发
- 客户端(Client):组装链并发送请求
一个真实的审批流程
我在做企业级OA系统时,遇到过典型的审批场景。请假申请需要根据天数走不同级别的审批:
- 3天以内:部门经理审批
- 3-7天:总监审批
- 7天以上:总经理审批
如果用if-else写,代码会变得又臭又长。而且每次调整审批规则,都要改核心逻辑。责任链模式正好派上用场。
Java 实现
// 抽象处理者
abstract class Approver {
protected Approver nextApprover;
protected String name;
public Approver(String name) {
this.name = name;
}
public void setNextApprover(Approver nextApprover) {
this.nextApprover = nextApprover;
}
public abstract void approve(int days);
}
// 具体处理者:部门经理
class Manager extends Approver {
public Manager(String name) {
super(name);
}
@Override
public void approve(int days) {
if (days <= 3) {
System.out.println(name + " 审批通过,请假天数:" + days);
} else if (nextApprover != null) {
nextApprover.approve(days);
}
}
}
// 具体处理者:总监
class Director extends Approver {
public Director(String name) {
super(name);
}
@Override
public void approve(int days) {
if (days <= 7) {
System.out.println(name + " 审批通过,请假天数:" + days);
} else if (nextApprover != null) {
nextApprover.approve(days);
}
}
}
// 具体处理者:总经理
class GeneralManager extends Approver {
public GeneralManager(String name) {
super(name);
}
@Override
public void approve(int days) {
if (days > 7) {
System.out.println(name + " 审批通过,请假天数:" + days);
} else if (nextApprover != null) {
nextApprover.approve(days);
}
}
}
// 客户端组装链
public class Client {
public static void main(String[] args) {
Approver manager = new Manager("张经理");
Approver director = new Director("李总监");
Approver generalManager = new GeneralManager("王总");
// 组装责任链
manager.setNextApprover(director);
director.setNextApprover(generalManager);
// 发送请求
manager.approve(2); // 张经理审批
manager.approve(5); // 李总监审批
manager.approve(10); // 王总审批
}
}
C++ 实现
#include <iostream>
#include <string>
using namespace std;
// 抽象处理者
class Approver {
protected:
Approver* nextApprover;
string name;
public:
Approver(string name) : name(name), nextApprover(nullptr) {}
void setNextApprover(Approver* next) { nextApprover = next; }
virtual void approve(int days) = 0;
virtual ~Approver() {}
};
// 部门经理
class Manager : public Approver {
public:
Manager(string name) : Approver(name) {}
void approve(int days) override {
if (days <= 3) {
cout << name << " 审批通过,请假天数:" << days << endl;
} else if (nextApprover != nullptr) {
nextApprover->approve(days);
}
}
};
// 总监
class Director : public Approver {
public:
Director(string name) : Approver(name) {}
void approve(int days) override {
if (days <= 7) {
cout << name << " 审批通过,请假天数:" << days << endl;
} else if (nextApprover != nullptr) {
nextApprover->approve(days);
}
}
};
// 总经理
class GeneralManager : public Approver {
public:
GeneralManager(string name) : Approver(name) {}
void approve(int days) override {
if (days > 7) {
cout << name << " 审批通过,请假天数:" << days << endl;
} else if (nextApprover != nullptr) {
nextApprover->approve(days);
}
}
};
int main() {
Approver* manager = new Manager("张经理");
Approver* director = new Director("李总监");
Approver* generalManager = new GeneralManager("王总");
manager->setNextApprover(director);
director->setNextApprover(generalManager);
manager->approve(2);
manager->approve(5);
manager->approve(10);
delete manager;
delete director;
delete generalManager;
return 0;
}
责任链的组装方式
链的组装其实就两种方式:
- 手动组装:客户端显式调用 setNext() 方法。优点是灵活,缺点是客户端要了解链的结构。
- 自动组装:通过配置文件或注解自动构建链。适合链比较固定的场景。
我个人更推荐手动组装,因为代码可读性更强。你一眼就能看出链的顺序。
过滤器链:另一个经典应用
除了审批流程,过滤器链也是责任链模式的典型应用。Java Servlet 中的 FilterChain 就是最好的例子。
我记得有一次做Web项目,需要实现请求的日志记录、权限校验、参数校验。如果用责任链模式,每个过滤器只关心自己的事:
interface Filter {
void doFilter(Request request, Response response, FilterChain chain);
}
class LogFilter implements Filter {
public void doFilter(Request request, Response response, FilterChain chain) {
System.out.println("记录日志:" + request.getUrl());
chain.doFilter(request, response); // 传给下一个
}
}
class AuthFilter implements Filter {
public void doFilter(Request request, Response response, FilterChain chain) {
if (request.getToken() != null) {
chain.doFilter(request, response);
} else {
System.out.println("未授权,拦截");
}
}
}
你看,每个过滤器只做一件事,互不干扰。想加新的过滤器?直接插到链里就行。
责任链模式的结构图
避坑指南
我曾经踩过的坑:
- 链的循环问题:有一次我不小心把链的最后一个节点的 next 指向了第一个节点,结果请求在链里死循环了。一定要确保链的终点有明确的终止条件。
- 处理顺序搞反:审批流程中,如果先让总经理处理,再让经理处理,那经理永远没机会。链的顺序决定了处理优先级。
- 忘记调用 next:在具体处理者中,如果条件不满足,一定要记得调用 nextApprover.handleRequest(),否则链就断了。
我的建议:
- 每个处理者只做一件事,遵循单一职责原则
- 链的长度不要超过 5-7 个节点,太长了影响性能
- 考虑使用建造者模式来组装链,代码会更优雅
什么时候用责任链模式?
| 场景 | 说明 |
|---|---|
| 审批流程 | 不同级别审批不同范围的请求 |
| 过滤器/拦截器 | Web 请求的预处理、后处理 |
| 日志框架 | 不同级别的日志输出到不同目的地 |
| 异常处理 | 不同类型的异常由不同处理器处理 |
嗯,责任链模式就是这么回事。它不复杂,但用好了能让代码的扩展性提升一大截。下次你遇到「多个处理者按顺序处理请求」的场景,不妨试试它。