根据 ChatGPT 的描述, 后端项目中出现频率最高的前 6 名设计模式是:
| 排名 | 模式 | 典型应用 |
|---|---|---|
| 🥇 1 | 单例模式 | 数据库连接、缓存客户端、全局配置 |
| 🥈 2 | 工厂模式 | 动态加载不同实现(如多租户、支付、存储) |
| 🥉 3 | 代理模式 / AOP | 事务、日志、鉴权、缓存 |
| 4 | 策略模式 | 不同业务策略(支付、路由、计算方式) |
| 5 | 模板方法模式 | 标准化业务流程 |
| 6 | 职责链模式 | 请求处理流水线、过滤器链(如 Spring FilterChain) |
今天是周六,周末愉快!更新常用设计模式系列最后一篇:职责链模式,本周写作任务也算圆满完成。
简介
职责链模式(Chain of Responsibility)是一种行为设计模式,它允许将请求沿着处理者链传递,直到被处理。适用于日志记录、权限验证等场景,避免请求发送者和接收者耦合。
核心思想
- 每个处理者决定是否处理请求,或传递给下一个。
- 链式结构,提高灵活性。
优点
- 降低耦合度:请求发送者无需知道哪个对象会处理它的请求,只需要将请求发送到链上即可。
- 增强灵活性:可以动态地改变链中处理者的顺序,或者增加、删除处理者。
- 简化对象职责:每个处理者只需要处理自己职责范围内的工作,不符合的请求直接转发。
- 便于扩展:新增处理者非常容易,符合开闭原则。
适用场景
职责链模式在以下场景中特别有用:
- 多级处理系统:如审批流程、异常处理、事件冒泡
- 不确定处理者的场景:需要动态指定处理对象
- 可插拔的处理逻辑:如中间件、过滤器链
TypeScript 示例
假设审批流程:员工请假,经理->总监->CEO。
classDiagram class Handler { -successor: Handler +setSuccessor(successor: Handler) +handleRequest(days: number)* } class Manager { +handleRequest(days: number) } class Director { +handleRequest(days: number) } class CEO { +handleRequest(days: number) } Handler <|-- Manager Handler <|-- Director Handler <|-- CEO Handler o--> "0..1" Handler : successor
typescript
// 处理者抽象类
abstract class Handler {
protected successor: Handler | null = null;
setSuccessor(successor: Handler): void {
this.successor = successor;
}
abstract handleRequest(days: number): void;
}
// 具体处理者:经理
class Manager extends Handler {
handleRequest(days: number): void {
if (days <= 3) {
console.log(`经理批准${days}天假`);
} else if (this.successor) {
this.successor.handleRequest(days);
} else {
console.log("无法批准");
}
}
}
// 总监
class Director extends Handler {
handleRequest(days: number): void {
if (days <= 7) {
console.log(`总监批准${days}天假`);
} else if (this.successor) {
this.successor.handleRequest(days);
} else {
console.log("无法批准");
}
}
}
// CEO
class CEO extends Handler {
handleRequest(days: number): void {
if (days <= 30) {
console.log(`CEO批准${days}天假`);
} else {
console.log("无法批准");
}
}
}
// 使用
const manager = new Manager();
const director = new Director();
const ceo = new CEO();
manager.setSuccessor(director);
director.setSuccessor(ceo);
manager.handleRequest(2); // 经理批准2天假
manager.handleRequest(5); // 总监批准5天假
manager.handleRequest(20); // CEO批准20天假
manager.handleRequest(40); // 无法批准
真实案例
alibaba/Sentinel
alibaba/Sentinel 面向云原生微服务的高可用流控防护组件。
Sentinel 的核心机制之一,就是依靠 Slot Chain(槽链) 构建的一条完整职责链,用于处理限流、熔断、热点参数、系统规则等逻辑。
这一套体系是典型的职责链模式工程化实现:
每个 Slot 是一个 Handler,SlotChain 是责任链,具体 Slot 按顺序依次执行,某些 Slot 可以中断链。
1. 抽象节点:AbstractLinkedProcessorSlot
这是所有 Slot 的抽象父类,职责链核心结构就在这里。
java
public abstract class AbstractLinkedProcessorSlot<T> implements ProcessorSlot<T> {
private ProcessorSlot<T> next;
@Override
public void setNext(ProcessorSlot<T> next) {
this.next = next;
}
@Override
public void entry(Context context, ResourceWrapper resourceWrapper, Object... args) throws Throwable {
// 当前 Slot 的处理逻辑(留给子类 override)
fireEntry(context, resourceWrapper, args);
}
protected void fireEntry(Context context, ResourceWrapper resourceWrapper, Object... args) throws Throwable {
if (next != null) {
next.entry(context, resourceWrapper, args);
}
}
@Override
public void exit(Context context, ResourceWrapper resourceWrapper, Object... args) {
// 当前 Slot 的 exit 逻辑
fireExit(context, resourceWrapper, args);
}
protected void fireExit(Context context, ResourceWrapper resourceWrapper, Object... args) {
if (next != null) {
next.exit(context, resourceWrapper, args);
}
}
}
这是典型 CoR:每个节点既处理,也决定是否继续传递。
- next 指向下一个 Slot → 链结构
- 当前 Slot 做完自己的逻辑后调用 next.entry() → 请求向后传递
- 如果某个 Slot 不调用 fireEntry,链会被中断(比如限流时抛出异常)
2. 责任链容器:ProcessorSlotChain
负责保存 slot 链的头节点,并按顺序执行 entry/exit。
java
public class ProcessorSlotChain extends AbstractLinkedProcessorSlot<Object> {
private ProcessorSlot<Object> first = null;
private ProcessorSlot<Object> end = null;
public void addFirst(ProcessorSlot<?> slot) {
slot.setNext(first);
first = (ProcessorSlot<Object>) slot;
if (end == null) {
end = first;
}
}
public void addLast(ProcessorSlot<?> slot) {
if (first == null) {
first = end = (ProcessorSlot<Object>) slot;
} else {
end.setNext(slot);
end = (ProcessorSlot<Object>) slot;
}
}
@Override
public void entry(Context context, ResourceWrapper resourceWrapper, Object... args) throws Throwable {
if (first != null) {
first.entry(context, resourceWrapper, args);
}
}
@Override
public void exit(Context context, ResourceWrapper resourceWrapper, Object... args) {
if (first != null) {
first.exit(context, resourceWrapper, args);
}
}
}
这是职责链容器的标准实现:
- first、end 维护了一条链表结构
- addLast / addFirst 构造责任链
- entry() 执行链首,之后顺链传递
3. 链构造器:DefaultSlotChainBuilder
负责按照既定顺序将各种 Slot 串成链。
java
public class DefaultSlotChainBuilder implements SlotChainBuilder {
@Override
public ProcessorSlotChain build() {
ProcessorSlotChain chain = new ProcessorSlotChain();
// 1. 选择调用链
chain.addLast(new NodeSelectorSlot());
// 2. 统计调用数据
chain.addLast(new ClusterBuilderSlot());
// 3. 流控规则检查
chain.addLast(new FlowSlot());
// 4. 熔断规则检查
chain.addLast(new DegradeSlot());
// 5. 系统规则
chain.addLast(new SystemSlot());
return chain;
}
}
职责链逻辑说明
- 这是固定顺序的 Slot pipeline
- 每个 Slot 是职责链中的一个 "处理节点"
- 每个 Slot 的功能独立、解耦
- 添加顺序决定了处理流程
- 完全符合 GoF CoR 的链构建方式
4. 一个典型 Slot 示例:FlowSlot
这里是限流规则检查。限流发生时直接中断链。
java
public class FlowSlot extends AbstractLinkedProcessorSlot<DefaultNode> {
@Override
public void entry(Context context, ResourceWrapper resourceWrapper, Object... args) throws Throwable {
// 检查是否触发限流
FlowRuleChecker.checkFlow(resourceWrapper, context, args);
// 没有被限流:继续到下一个 slot
fireEntry(context, resourceWrapper, args);
}
}
职责链行为说明
- FlowRuleChecker.checkFlow 若触发限流会抛异常
- 链处理被 终止(典型 CoR:某节点可终止请求处理)
- 未限流 → 调用 fireEntry() → 继续下一个 slot
这是职责链里典型的"拦截节点"。
5. 请求进入链:Sentinel 核心调用逻辑
CtSph.entry() 调用 slot 链的入口
java
ProcessorSlotChain chain = SlotChainProvider.newSlotChain();
chain.entry(context, resourceWrapper, args);
- 请求进入链首
- 逐个 slot 执行
- 期间任何 slot 抛 BlockException → 链终止
总结
flowchart LR C(Context) --> PSC[ProcessorSlotChain] PSC --> NS[NodeSelectorSlot] NS --> CB[ClusterBuilderSlot] CB --> FS[FlowSlot] FS --> DS[DegradeSlot] DS --> SS[SystemSlot] SS --> E(Exit)
- Handler(处理节点): ProcessorSlot & 各种具体 Slot(FlowSlot、DegradeSlot...)
- Handler 基类: AbstractLinkedProcessorSlot
- 链(责任链): ProcessorSlotChain
- 链构造器: DefaultSlotChainBuilder
- 请求入口: CtSph.entry() 调用 SlotChain
Sentinel 通过 SlotChain 实现:
- 可插拔 Slot
- 多个 Slot 组成 pipeline
- Slot 可决定是否继续传递请求(关键的 CoR 特征)
- Slot 之间松耦合,遵循开闭原则
- 真实工程中的高复杂度职责链,结构清晰可扩展
poppinss/middleware
poppinss/middleware本身就是一个 CoR(middleware pipeline)的实现。
typescript
import Middleware from '@poppinss/middleware'
import { NextFn } from '@poppinss/middleware/types'
const context = {}
type MiddlewareFn = (ctx: typeof context, next: NextFn) => void | Promise<void>
const middleware = new Middleware<MiddlewareFn>()
middleware.add((ctx, next) => {
console.log('executing fn1')
await next()
})
middleware.add((ctx, next) => {
console.log('executing fn2')
await next()
})
await middleware
.runner()
.run((fn, next) => fn(context, next))
const middleware = new Middleware<MiddlewareFn>():创建一个 Middleware 实例。这个类正是 CoR 的核心 --- 它管理一个链(chain)。middleware.add((ctx, next) => { ... }):通过 add 方法将一个中间件处理函数加入链中。对应 CoR 模式里"将一个处理器(handler)加入链"。await next():在中间件内部调用 next(),表示把控制权传给链上的下一个处理器。正是 CoR "传递请求给下一个处理者"的机制。.runner().run((fn, next) => fn(context, next)):创建一个 runner 来依次触发链上的处理器。run 方法遍历整个链,调度每一个中间件。
总结
职责链模式通过将请求的发送者和接收者解耦,提供了一种灵活的处理请求的方式。它在需要多级处理、动态指定处理者或实现可插拔架构的场景中表现出色。
在实际开发中,你可以根据具体需求选择适合的实现方式,无论是经典的固定链结构,还是更灵活的通用处理链,职责链模式都能为你的系统带来更好的可维护性和扩展性。