模式补全
这一节按 八面剖析以理解一个概念 / ljg-learn 的思路整理为 Markdown 版本:先定锚,再用八个方向切开概念,最后压缩成公式、例子、类图和检验题。
定锚
责任链模式(行为型)的通行定义:把请求沿着处理者链传递,直到某个处理者处理它或链条结束。
常见误解:责任链不是 if else 的机械换皮;它让处理节点可插拔、可重排。
核心词素:Chain of Responsibility 的重点是 chain:责任不是集中点,而是一条路径。
八刀
历史
它常见于 GUI 事件冒泡、审批流、中间件。GoF 将这种传递式处理抽成行为型模式。
辩证
反面是一个函数处理所有分支。更高理解是:请求处理权可以在对象之间流动。
现象
报销单先到组长,再到经理,再到财务;金额不同,停在不同节点。
语言
Chain of Responsibility 的重点是 chain:责任不是集中点,而是一条路径。
形式
handler.handle(request) or next.handle(request)。若链路不透明,调试会变困难。
存在
它让系统承认“不是每个对象都该知道所有规则”,每个节点只守自己的门。
美感
它美在接力:请求像火炬,从一只手传到下一只手。
元反思
链条隐喻会让人默认线性。换成“管道”隐喻,会更关注顺序、短路和错误传播。
内观
我只处理我能处理的请求。处理不了,我把它交给下一个。责任沿着链移动,直到找到位置。
八刀共同指向的深层结构:责任链模式不是为了“炫技”,而是在某个变化点上建立边界,让稳定部分继续稳定,让变化部分有自己的位置。
压缩
公式:责任链 = 处理者接口 + next 指针 + 可传递请求
一句话:责任链把集中判断拆成一串可组合的处理节点。
结构图:
Request -> HandlerA -> HandlerB -> HandlerC动机/意图
当一个请求可能由多个对象中的某一个处理,或者需要经过一串处理步骤时,集中分支会变得僵硬。责任链模式的意图是把处理者串成链,请求沿链传递,直到被处理或走完整条链。
结构/角色
Handler:处理者接口,声明处理请求和设置下一个节点的方法。BaseHandler:基础处理者,可选角色,保存next并提供默认传递逻辑。ConcreteHandler:具体处理者,判断自己能否处理请求,不能则转交下一个。Client:客户端组装链条并把请求交给链首。Request:请求对象,携带处理所需的数据。
典型 UML
classDiagram class Handler { <<interface>> +setNext(handler: Handler): Handler +handle(request): void } class BaseHandler { -next: Handler +setNext(handler): Handler +handle(request): void } class ConcreteHandlerA class ConcreteHandlerB Handler <|.. BaseHandler BaseHandler <|-- ConcreteHandlerA BaseHandler <|-- ConcreteHandlerB BaseHandler --> Handler : next
使用场景
- 请求处理者不固定,需要运行时动态组合顺序。
- 多个处理步骤可串联,例如鉴权、校验、限流、日志。
- 希望发送者和接收者解耦。
- 处理逻辑需要短路,某个节点处理后不再继续传递。
正例:TypeScript
这个场景里,请假单会沿着“主任 → 经理 → 总经理”这条链往后传。每个审批人只关心自己能否处理当前天数,处理不了就交给下一个;如果超过 30 天,则由总经理节点给出明确拒绝信息。
class LeaveRequest {
constructor(
public readonly employeeName: string,
public readonly days: number,
) {}
}
abstract class Approver {
private next?: Approver
setNext(next: Approver) {
this.next = next
return next
}
handle(request: LeaveRequest) {
if (this.canApprove(request.days)) {
this.approve(request)
return
}
if (this.next) {
this.next.handle(request)
return
}
console.log(`${request.employeeName} leave request is rejected`)
}
protected abstract canApprove(days: number): boolean
protected abstract approve(request: LeaveRequest): void
}
class Director extends Approver {
protected canApprove(days: number) {
return days < 3
}
protected approve(request: LeaveRequest) {
console.log(`director approves ${request.employeeName}'s ${request.days} day(s) leave`)
}
}
class Manager extends Approver {
protected canApprove(days: number) {
return days >= 3 && days < 10
}
protected approve(request: LeaveRequest) {
console.log(`manager approves ${request.employeeName}'s ${request.days} day(s) leave`)
}
}
class GeneralManager extends Approver {
protected canApprove(days: number) {
return days >= 10 && days < 30
}
protected approve(request: LeaveRequest) {
console.log(`general manager approves ${request.employeeName}'s ${request.days} day(s) leave`)
}
override handle(request: LeaveRequest) {
if (request.days >= 30) {
console.log(`general manager rejects ${request.employeeName}'s leave request`)
return
}
super.handle(request)
}
}
const director = new Director()
const manager = new Manager()
const generalManager = new GeneralManager()
director.setNext(manager).setNext(generalManager)
director.handle(new LeaveRequest("Alice", 2))
director.handle(new LeaveRequest("Bob", 7))
director.handle(new LeaveRequest("Cindy", 20))
director.handle(new LeaveRequest("David", 35))正例:UML 类图
classDiagram class LeaveRequest { +employeeName: string +days: number } class Approver { -next: Approver +setNext(next: Approver): Approver +handle(request: LeaveRequest): void #canApprove(days: number): boolean #approve(request: LeaveRequest): void } class Director { +handle(request: LeaveRequest): void } class Manager { +handle(request: LeaveRequest): void } class GeneralManager { +handle(request: LeaveRequest): void } Director --|> Approver Manager --|> Approver GeneralManager --|> Approver Approver --> Approver : next Approver ..> LeaveRequest
反例:TypeScript
反例里所有审批规则都挤在一个函数里。只要审批层级、阈值或拒绝规则一变,就必须回到这个中心函数继续堆分支。
function approveLeave(employeeName: string, days: number) {
if (days < 3) {
console.log(`director approves ${employeeName}'s leave`)
} else if (days >= 3 && days < 10) {
console.log(`manager approves ${employeeName}'s leave`)
} else if (days >= 10 && days < 30) {
console.log(`general manager approves ${employeeName}'s leave`)
} else {
console.log(`general manager rejects ${employeeName}'s leave request`)
}
}反例:UML 类图
classDiagram class LeaveApprovalService { +approveLeave(employeeName: string, days: number): void } LeaveApprovalService ..> LeaveApprovalService : growing if/else branches
案例
某OA系统需要提供一个假条审批的模块,如果员工请假天数小于3天,主任可以审批该假条;如果员工请假天数大于等于3天,小于10天,经理可以审批;如果员工请假天数大于等于10天,小于30天,总经理可以审批;如果超过30天,总经理也不能审批,提示相应的拒绝信息。
掌握检验
- 责任链和普通 if else 的本质差别是什么?
- 责任链中请求一定要被处理吗?
- Express/Koa 中间件为什么像责任链?