Chapter 13 — 责任链模式 Chain of Responsibility
灵魂速记:踢皮球——一个处理不了,就传给下一个,直到有人接手。
秒懂类比
你在公司报销:
- 金额 ≤ 500 →组长直接批
- 500 < 金额 ≤ 5000 → 组长签不了,传给经理
- 5000 < 金额 ≤ 50000 → 经理签不了,传给总监
- 金额 > 50000 → 总监签不了,传给CEO
每一级要么自己处理,要么传给上级。请求沿着链一直走,直到有人能处理。
问题引入
// 灾难现场:一堆 if-else 嵌套voidhandleRequest(Request&req){if(req.type=="spam"){// 过滤垃圾邮件}elseif(req.type=="auth"){// 检查认证}elseif(req.type=="rateLimit"){// 限流}elseif(req.type=="logging"){// 日志}// 每加一种处理,改这个函数……// 处理顺序硬编码,无法灵活调整}模式结构
┌───────────┐ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ Handler A │───→│ Handler B │───→│ Handler C │───→│ null │ │ (组长) │ │ (经理) │ │ (总监) │ │ (链尾) │ └───────────┘ └───────────┘ └───────────┘ └───────────┘ │ │ │ 能处理? 能处理? 能处理? ├─是→处理完毕 ├─是→处理完毕 ├─是→处理完毕 └─否→传下去 └─否→传下去 └─否→无人处理C++ 实现
#include<iostream>#include<memory>#include<string>// ========== 处理者基类 ==========classApprover{public:virtual~Approver()=default;std::shared_ptr<Approver>setNext(std::shared_ptr<Approver>next){next_=next;returnnext;// 返回下一个处理者,支持链式设置}voidhandle(conststd::string&applicant,doubleamount){if(canApprove(amount)){approve(applicant,amount);}elseif(next_){std::cout<<" "<<name()<<": ¥"<<amount<<" 超出我的权限,往上报\n";next_->handle(applicant,amount);}else{std::cout<<" ❌ 没人能批准 "<<applicant<<" 的 ¥"<<amount<<" 报销\n";}}protected:virtualboolcanApprove(doubleamount)const=0;virtualstd::stringname()const=0;voidapprove(conststd::string&applicant,doubleamount){std::cout<<" ✅ "<<name()<<" 批准了 "<<applicant<<" 的 ¥"<<amount<<" 报销\n";}private:std::shared_ptr<Approver>next_;};// ========== 具体处理者 ==========classTeamLead:publicApprover{protected:boolcanApprove(doubleamount)constoverride{returnamount<=500;}std::stringname()constoverride{return"组长";}};classManager:publicApprover{protected:boolcanApprove(doubleamount)constoverride{returnamount<=5000;}std::stringname()constoverride{return"经理";}};classDirector:publicApprover{protected:boolcanApprove(doubleamount)constoverride{returnamount<=50000;}std::stringname()constoverride{return"总监";}};classCEO:publicApprover{protected:boolcanApprove(doubleamount)constoverride{returnamount<=500000;}std::stringname()constoverride{return"CEO";}};intmain(){// 组装责任链autoceo=std::make_shared<CEO>();autodirector=std::make_shared<Director>();automanager=std::make_shared<Manager>();autolead=std::make_shared<TeamLead>();lead->setNext(manager)->setNext(director)->setNext(ceo);// 提交报销std::cout<<"=== 报销 ¥200 ===\n";lead->handle("张三",200);std::cout<<"\n=== 报销 ¥3000 ===\n";lead->handle("李四",3000);std::cout<<"\n=== 报销 ¥30000 ===\n";lead->handle("王五",30000);std::cout<<"\n=== 报销 ¥800000 ===\n";lead->handle("赵六",800000);}输出:
=== 报销 ¥200 === ✅ 组长 批准了 张三 的 ¥200 报销 === 报销 ¥3000 === 组长: ¥3000 超出我的权限,往上报 ✅ 经理 批准了 李四 的 ¥3000 报销 === 报销 ¥30000 === 组长: ¥30000 超出我的权限,往上报 经理: ¥30000 超出我的权限,往上报 ✅ 总监 批准了 王五 的 ¥30000 报销 === 报销 ¥800000 === 组长: ¥800000 超出我的权限,往上报 经理: ¥800000 超出我的权限,往上报 总监: ¥800000 超出我的权限,往上报 CEO: ¥800000 超出我的权限,往上报 ❌ 没人能批准 赵六 的 ¥800000 报销另一种用法:中间件管道
责任链不只是"找人处理",还可以是"每个人都处理一部分"(像流水线):
classMiddleware{public:virtual~Middleware()=default;std::shared_ptr<Middleware>linkWith(std::shared_ptr<Middleware>next){next_=next;returnnext;// 返回下一个中间件,支持链式设置}boolcheck(conststd::string&request){if(!doCheck(request))returnfalse;// 自己拦截了if(next_)returnnext_->check(request);// 传给下一个returntrue;// 全部通过}protected:virtualbooldoCheck(conststd::string&request)=0;private:std::shared_ptr<Middleware>next_;};Web 框架中的中间件(认证 → 限流 → 日志 → 业务逻辑)就是这种模式。
什么时候用?
| ✅ 适合 | ❌ 别用 |
|---|---|
| 多个对象都可能处理请求 | 只有一个处理者 |
| 处理者和顺序需要运行时灵活调整 | 处理逻辑固定不变 |
| 不希望发送者知道谁会处理 | 发送者必须知道处理者 |
| 中间件/过滤器管道 | 所有处理者都必须执行 |
防混淆
责任链 vs 装饰器
| 责任链 | 装饰器 | |
|---|---|---|
| 结构 | 都是链式 | 都是链式 |
| 传递 | 可以中断(有人处理了就停) | 不中断(每层都执行) |
| 目的 | 找到能处理的人 | 层层增强功能 |
一句话分清:责任链可以停,装饰器必须走完。
责任链 vs Command
| 责任链 | Command | |
|---|---|---|
| 请求去向 | 沿链传递,不确定谁处理 | 直接发给指定的接收者 |
| 处理者数量 | 多个候选 | 一个确定的 |