前言:那些年我们写过的"屎山 if-else"
相信每一个后端程序员都有过这样的经历:
public void processRequest(Request request) {
if (!checkLogin(request)) {
throw new UnauthorizedException("没登录,回家洗洗睡");
}
if (!checkPermission(request)) {
throw new ForbiddenException("没权限,洗洗也睡");
}
if (!checkRateLimit(request)) {
throw new TooManyRequestsException("别刷了,睡觉");
}
if (!checkBlacklist(request)) {
throw new BlockedException("你已被拉黑,永久失眠");
}
// 终于到业务逻辑了……
doActualWork(request);
}改需求了,加一个风控校验?找到那个 processRequest,小心翼翼地在第 3 行和第 4 行之间插一段 if。
三个月后,新来的同事盯着这 200 行的方法,欲言又止,最后只说了一句:"这写的什么鬼……"
这就是责任链模式要解决的问题。
一、什么是责任链模式?
将请求的发送者与处理者解耦,让多个对象都有机会处理请求,并将这些对象连成一条链,请求沿着链传递,直到被处理为止。
用大白话说:流水线工人 模型。
每个工人(Handler)只干自己那摊事:
- 能处理 → 处理
- 不能处理 / 不归我管 → 丢给下一个工人
没有任何一个工人需要知道整条流水线长什么样,更不用知道你后面还有谁。
二、核心结构
classDiagram
class Handler {
<>
-Handler nextHandler
+setNext(handler: Handler) Handler
+handle(request: Request) Response
}
class ConcreteHandlerA {
+handle(request: Request) Response
}
class ConcreteHandlerB {
+handle(request: Request) Response
}
class ConcreteHandlerC {
+handle(request: Request) Response
}
class Client {
+sendRequest(request: Request)
}
Handler <|-- ConcreteHandlerA
Handler <|-- ConcreteHandlerB
Handler <|-- ConcreteHandlerC
Handler o--> Handler : nextHandler
Client --> Handler : uses
核心角色就三个:
| 角色 | 职责 |
|---|---|
| Handler(抽象处理者) | 定义处理接口,持有下一个处理者的引用 |
| ConcreteHandler(具体处理者) | 实现自己的处理逻辑,决定是否传递给下一个 |
| Client(客户端) | 组装责任链,发起请求 |
三、请求流转图
以 API 网关鉴权流程 为例,一条典型的责任链长这样:
flowchart LR
C([📱 客户端请求]) --> A
subgraph Chain ["🔗 责任链"]
A[🔑 登录校验
AuthHandler]
B[🛡️ 权限校验
PermissionHandler]
D[⏱️ 限流校验
RateLimitHandler]
E[🚫 黑名单校验
BlacklistHandler]
F[💼 业务处理
BusinessHandler]
A -->|通过| B
B -->|通过| D
D -->|通过| E
E -->|通过| F
end
A -->|❌ 拦截| R1([401 未登录])
B -->|❌ 拦截| R2([403 无权限])
D -->|❌ 拦截| R3([429 限流])
E -->|❌ 拦截| R4([403 已拉黑])
F --> R5([✅ 200 成功])
每个 Handler 干完自己的活,要么拦截,要么放行。业务逻辑完全不用知道前面发生过什么。
四、代码实现(Go 版本)
为什么用 Go?因为 Joey 喜欢 Go。从入门到放弃系列:Go 篇早就放弃了,但责任链还没放弃。
4.1 定义 Handler 接口
package chain
// Request 请求上下文
type Request struct {
UserID string
Token string
IP string
Path string
Payload map[string]interface{}
}
// Response 响应
type Response struct {
Code int
Message string
Data interface{}
}
// Handler 处理者接口
type Handler interface {
SetNext(handler Handler) Handler
Handle(req *Request) *Response
}4.2 抽象基类(减少重复)
// BaseHandler 基础处理者,提供链接能力
type BaseHandler struct {
next Handler
}
func (b *BaseHandler) SetNext(handler Handler) Handler {
b.next = handler
return handler // 返回 handler 支持链式调用
}
// PassToNext 传递给下一个处理者
func (b *BaseHandler) PassToNext(req *Request) *Response {
if b.next != nil {
return b.next.Handle(req)
}
// 链条末端,默认放行
return &Response{Code: 200, Message: "OK"}
}4.3 具体 Handler 实现
// ---- 登录校验 ----
type AuthHandler struct {
BaseHandler
}
func (h *AuthHandler) Handle(req *Request) *Response {
if req.Token == "" {
return &Response{Code: 401, Message: "未登录,你是谁?"}
}
// 实际项目里这里验 JWT
fmt.Println("[Auth] 登录校验通过 ✅")
return h.PassToNext(req)
}
// ---- 权限校验 ----
type PermissionHandler struct {
BaseHandler
allowedPaths map[string][]string // userRole -> []path
}
func (h *PermissionHandler) Handle(req *Request) *Response {
// 简化:通过 Token 长度判断权限(别学我,这只是 demo)
if len(req.Token) < 10 {
return &Response{Code: 403, Message: "权限不足,你配吗?"}
}
fmt.Println("[Permission] 权限校验通过 ✅")
return h.PassToNext(req)
}
// ---- 限流校验 ----
type RateLimitHandler struct {
BaseHandler
counter map[string]int // IP -> 请求次数(真实项目用 Redis)
limit int
}
func (h *RateLimitHandler) Handle(req *Request) *Response {
h.counter[req.IP]++
if h.counter[req.IP] > h.limit {
return &Response{Code: 429, Message: "太快了,缓一缓"}
}
fmt.Println("[RateLimit] 限流校验通过 ✅")
return h.PassToNext(req)
}
// ---- 黑名单校验 ----
type BlacklistHandler struct {
BaseHandler
blacklist map[string]bool
}
func (h *BlacklistHandler) Handle(req *Request) *Response {
if h.blacklist[req.IP] {
return &Response{Code: 403, Message: "你已被列入黑名单,永久告别"}
}
fmt.Println("[Blacklist] 黑名单校验通过 ✅")
return h.PassToNext(req)
}
// ---- 业务处理(终点) ----
type BusinessHandler struct {
BaseHandler
}
func (h *BusinessHandler) Handle(req *Request) *Response {
fmt.Println("[Business] 终于轮到我了,处理业务逻辑 🎉")
return &Response{Code: 200, Message: "success", Data: "业务数据"}
}4.4 组装责任链
func main() {
// 1. 创建各个处理者
auth := &AuthHandler{}
permission := &PermissionHandler{}
rateLimit := &RateLimitHandler{
counter: make(map[string]int),
limit: 100,
}
blacklist := &BlacklistHandler{
blacklist: map[string]bool{"1.2.3.4": true},
}
business := &BusinessHandler{}
// 2. 组装链(链式调用,优雅!)
auth.SetNext(permission).
SetNext(rateLimit).
SetNext(blacklist).
SetNext(business)
// 3. 发起请求
req := &Request{
UserID: "joey",
Token: "eyJhbGciOiJIUzI1NiJ9.xxx",
IP: "10.0.0.1",
Path: "/api/payment",
}
resp := auth.Handle(req)
fmt.Printf("Response: %d %s\n", resp.Code, resp.Message)
}输出:
[Auth] 登录校验通过 ✅
[Permission] 权限校验通过 ✅
[RateLimit] 限流校验通过 ✅
[Blacklist] 黑名单校验通过 ✅
[Business] 终于轮到我了,处理业务逻辑 🎉
Response: 200 success五、真实场景:多级审批工作流
这是责任链最经典的场景之一。以报销审批为例:
stateDiagram-v2
[*] --> 提交申请
提交申请 --> 直属领导审批: 金额 ≤ 1000
直属领导审批 --> 通过 : ✅ 批准
直属领导审批 --> 拒绝 : ❌ 驳回
直属领导审批 --> 部门经理审批 : 金额 > 1000
部门经理审批 --> 通过 : ✅ 批准(≤ 5000)
部门经理审批 --> 拒绝 : ❌ 驳回
部门经理审批 --> CFO审批 : 金额 > 5000
CFO审批 --> 通过 : ✅ 批准
CFO审批 --> 拒绝 : ❌ 驳回
通过 --> [*]
拒绝 --> [*]
type ApprovalRequest struct {
Amount float64
Description string
ApplicantID string
}
// TeamLeaderHandler 直属领导:审批 1000 以内
type TeamLeaderHandler struct {
BaseHandler
}
func (h *TeamLeaderHandler) Handle(req *ApprovalRequest) string {
if req.Amount <= 1000 {
return fmt.Sprintf("✅ 直属领导批准:%.0f 元报销通过", req.Amount)
}
fmt.Printf("[TeamLeader] %.0f 元超出权限,上报部门经理\n", req.Amount)
return h.PassToNext(req) // 超过权限,往上传
}
// ManagerHandler 部门经理:审批 5000 以内
type ManagerHandler struct {
BaseHandler
}
func (h *ManagerHandler) Handle(req *ApprovalRequest) string {
if req.Amount <= 5000 {
return fmt.Sprintf("✅ 部门经理批准:%.0f 元报销通过", req.Amount)
}
fmt.Printf("[Manager] %.0f 元超出权限,上报 CFO\n", req.Amount)
return h.PassToNext(req)
}
// CFOHandler CFO:无上限(CFO 也是人,也会皱眉头)
type CFOHandler struct {
BaseHandler
}
func (h *CFOHandler) Handle(req *ApprovalRequest) string {
if req.Amount > 50000 {
return fmt.Sprintf("❌ CFO 拒绝:%.0f 元?你是来抢钱的吗?", req.Amount)
}
return fmt.Sprintf("✅ CFO 批准:%.0f 元,下不为例", req.Amount)
}六、与 Middleware 的关系
如果你用过 Gin、Echo、Koa、Express,你天天在用责任链,只是你可能叫它 Middleware(中间件)。
sequenceDiagram
participant C as 客户端
participant M1 as Logger 中间件
participant M2 as Auth 中间件
participant M3 as Cors 中间件
participant H as 业务 Handler
C->>M1: HTTP Request
M1->>M1: 记录请求日志
M1->>M2: next()
M2->>M2: 验证 Token
M2->>M3: next()
M3->>M3: 添加跨域头
M3->>H: next()
H->>H: 执行业务逻辑
H-->>M3: Response
M3-->>M2: Response
M2-->>M1: Response
M1->>M1: 记录响应日志
M1-->>C: HTTP Response
Gin 的中间件本质上就是责任链的一种变体,只不过它是双向的(请求向下传,响应向上回)。
// Gin 中间件 = 责任链的工程实践
r := gin.New()
r.Use(
middleware.Logger(), // Handler 1
middleware.Auth(), // Handler 2
middleware.RateLimit(), // Handler 3
)
r.GET("/api/payment", handler.Payment)七、优缺点分析
✅ 优点
mindmap
root((责任链优点))
解耦
发送者不知道谁处理
处理者不知道链有多长
灵活扩展
新增步骤只需写新 Handler
不修改已有代码
符合开闭原则
单一职责
每个 Handler 只关心自己
代码清晰可维护
顺序可控
组装时决定顺序
运行时动态调整链
❌ 缺点
| 问题 | 说明 |
|---|---|
| 调试困难 | 请求在链中流转,出问题不好定位(加日志!) |
| 性能开销 | 链条过长时每个节点都要处理,注意性能 |
| 请求可能未被处理 | 如果所有 Handler 都不处理,请求会悄悄消失 |
| 链条管理成本 | 链条顺序错误会导致逻辑 bug,需统一管理 |
八、与其他模式对比
quadrantChart
title 设计模式选型参考
x-axis 请求是否需要多个处理者 --> 单个处理者足够
y-axis 处理者是否需要解耦 --> 处理者紧密耦合
责任链: [0.15, 0.85]
装饰器: [0.3, 0.4]
策略模式: [0.75, 0.6]
命令模式: [0.6, 0.3]
观察者: [0.2, 0.2]
| 模式 | 核心区别 |
|---|---|
| 责任链 | 请求沿链传递,每个节点可拦截,处理者间解耦 |
| 装饰器 | 层层包裹增强功能,每层都会执行,不会中断 |
| 策略模式 | 选择一个策略执行,不传递请求 |
| 命令模式 | 将请求封装为对象,支持撤销/重做 |
九、工程实践建议
在实际 支付/金融系统 中使用责任链,有几个踩过的坑要特别提一下:
9.1 务必打印链条日志
func (b *BaseHandler) PassToNext(req *Request) *Response {
if b.next != nil {
log.Printf("[Chain] %T → %T", b, b.next) // 打印链路
return b.next.Handle(req)
}
return &Response{Code: 200, Message: "OK"}
}9.2 链条末端要有兜底
// 末端 Handler,防止请求"消失"
type FallbackHandler struct{}
func (h *FallbackHandler) Handle(req *Request) *Response {
log.Warnf("[Chain] 请求未被任何 Handler 处理: %+v", req)
return &Response{Code: 500, Message: "内部错误:没有人处理你的请求"}
}9.3 链条顺序要集中管理
// chain/factory.go —— 统一在这里组装链,不要散落各处
func BuildAPIChain() Handler {
auth := &AuthHandler{}
permission := &PermissionHandler{}
rateLimit := &RateLimitHandler{limit: 100}
blacklist := &BlacklistHandler{}
business := &BusinessHandler{}
fallback := &FallbackHandler{}
auth.SetNext(permission).
SetNext(rateLimit).
SetNext(blacklist).
SetNext(business).
SetNext(fallback)
return auth
}9.4 支付系统中的应用
在 NPSS/Aani 这类即时支付场景中,责任链的变体随处可见:
flowchart TD
A[收到支付请求] --> B[IBAN 格式校验]
B -->|✅| C[账户状态校验]
C -->|✅| D[余额校验]
D -->|✅| E[风控规则校验]
E -->|✅| F[合规 AML 校验]
F -->|✅| G[路由到支付渠道]
G --> H[✅ 发起支付]
B -->|❌| X1[拒绝: IBAN 格式错误]
C -->|❌| X2[拒绝: 账户已冻结]
D -->|❌| X3[拒绝: 余额不足]
E -->|❌| X4[拒绝: 风控拦截]
F -->|❌| X5[拒绝: AML 命中]
style H fill:#22c55e,color:#fff
style X1 fill:#ef4444,color:#fff
style X2 fill:#ef4444,color:#fff
style X3 fill:#ef4444,color:#fff
style X4 fill:#ef4444,color:#fff
style X5 fill:#ef4444,color:#fff
每个节点都可以单独测试、单独监控、单独上线。新加一个 欺诈检测 节点?在风控和 AML 之间插入即可,其他代码零修改。
十、总结
责任链模式 的精髓是:每个人只负责自己该负责的,剩下的不是我的事。
这听起来像是甩锅,但在系统设计里,这叫 单一职责,这叫 解耦,这叫 优雅。
当你下次看到一个长达几百行、全是 if-else 的校验方法时,不要骂写这段代码的人——可能那个人就是三个月前的你。
把它重构成责任链吧,让未来的你感谢今天的你。
从入门到放弃系列的精神:
入门——你写 if-else
进阶——你写责任链
放弃——你写框架让别人写责任链
顿悟——你发现框架里还是 if-else