14、Python - 责任链模式

文章目录
- [14、Python - 责任链模式](#14、Python - 责任链模式)
-
- [一、痛点场景:你的代码是不是也被 if-else 淹没了?](#一、痛点场景:你的代码是不是也被 if-else 淹没了?)
- [二、痛点的解决方案:把 if-else 拆成一条链](#二、痛点的解决方案:把 if-else 拆成一条链)
- 三、是什么:专业解释、大白话和生活案例
-
- [3.1 专业解释](#3.1 专业解释)
- [3.2 大白话解释](#3.2 大白话解释)
- [3.3 生活案例](#3.3 生活案例)
- 四、为什么用:责任链模式解决了什么问题
-
- [4.1 核心价值](#4.1 核心价值)
- [4.2 设计原则的体现](#4.2 设计原则的体现)
- [4.3 什么时候该用](#4.3 什么时候该用)
- [五、它是怎么演进过来的:从 if-else 到中间件](#五、它是怎么演进过来的:从 if-else 到中间件)
-
- [5.1 第一阶段:朴素的 if-else](#5.1 第一阶段:朴素的 if-else)
- [5.2 第二阶段:列表循环](#5.2 第二阶段:列表循环)
- [5.3 第三阶段:经典责任链模式](#5.3 第三阶段:经典责任链模式)
- [5.4 第四阶段:不纯责任链(过滤器/中间件)](#5.4 第四阶段:不纯责任链(过滤器/中间件))
- [5.5 第五阶段:Pipeline / 管道模式](#5.5 第五阶段:Pipeline / 管道模式)
- [六、怎么用:Python 完整实现](#六、怎么用:Python 完整实现)
-
- [6.1 经典实现(纯责任链)](#6.1 经典实现(纯责任链))
- [6.2 不纯责任链实现(过滤器/中间件风格)](#6.2 不纯责任链实现(过滤器/中间件风格))
- [6.3 Pythonic 实现(函数式 + 装饰器)](#6.3 Pythonic 实现(函数式 + 装饰器))
- 七、企业项目实战:订单审核系统
-
- [7.1 业务背景](#7.1 业务背景)
- [7.2 代码实现](#7.2 代码实现)
- [7.3 这个设计好在哪](#7.3 这个设计好在哪)
- [八、竞品对比:责任链 vs 装饰器 vs 策略 vs 观察者](#八、竞品对比:责任链 vs 装饰器 vs 策略 vs 观察者)
-
- [8.1 核心区别对比表](#8.1 核心区别对比表)
- [8.2 用生活案例理解区别](#8.2 用生活案例理解区别)
- [8.3 代码结构对比](#8.3 代码结构对比)
- [8.4 怎么选](#8.4 怎么选)
- 九、常用场景总结
-
- [9.1 Web开发场景](#9.1 Web开发场景)
- [9.2 业务系统场景](#9.2 业务系统场景)
- [9.3 数据处理场景](#9.3 数据处理场景)
- [9.4 框架源码中的应用](#9.4 框架源码中的应用)
- 十、面试官高频面试题
- 十一、总结
一、痛点场景:你的代码是不是也被 if-else 淹没了?
假设你在一家电商公司负责订单系统。产品经理提了一个需求:用户下单时需要做一系列校验。
你一开始写得很清爽:
python
def create_order(user, product):
# 校验用户是否登录
if not user.is_login:
raise Exception("请先登录")
# 校验库存
if product.stock <= 0:
raise Exception("库存不足")
# 校验余额
if user.balance < product.price:
raise Exception("余额不足")
# 创建订单
return Order(user, product)
看起来没问题对吧?但一周后,产品经理说:
- 要加一个风控校验,防止恶意刷单
- 要加一个优惠券校验
- 要加一个地区配送校验
- VIP 用户要跳过某些校验
- 大促期间要临时加一个限购校验
你的代码变成了这样:
python
def create_order(user, product, coupon=None):
if not user.is_login:
raise Exception("请先登录")
if user.is_blocked:
raise Exception("账号已被封禁")
if product.stock <= 0:
raise Exception("库存不足")
if not user.is_vip:
if user.balance < product.price:
raise Exception("余额不足")
if coupon:
if coupon.is_expired:
raise Exception("优惠券已过期")
if coupon.min_amount > product.price:
raise Exception("未达到优惠券使用门槛")
if user.region not in product.support_regions:
raise Exception("该地区暂不支持配送")
if is_promotion_period():
if user.daily_order_count >= 3:
raise Exception("大促期间每人限购3单")
# 还有20个校验...
return Order(user, product)
这就是典型的"面条式代码"。问题在哪?
- 一个函数干了所有事:登录校验、库存校验、余额校验、风控校验全挤在一起,违反单一职责原则
- 改一个需求要动整个函数:加一个校验就要在函数中间插代码,容易引入bug
- 无法灵活组合:VIP用户要跳过余额校验,大促要加限购,你只能写更多 if-else
- 无法复用:这些校验逻辑在退款、换货流程里也要用,但你只能复制粘贴
- 测试困难:要测一个校验逻辑,必须把整个函数跑起来
这不是你一个人的问题,这是所有流程化业务的通病。而责任链模式,就是来解决这个问题的。
二、痛点的解决方案:把 if-else 拆成一条链
责任链模式的核心思路很简单:把每个校验逻辑拆成独立的处理者,然后把它们串成一条链。请求从链头进入,依次经过每个处理者,每个处理者决定自己处理还是传给下一个。
用责任链模式重构后,代码变成这样:
python
class LoginHandler:
def handle(self, user, product, coupon=None):
if not user.is_login:
raise Exception("请先登录")
return True
class StockHandler:
def handle(self, user, product, coupon=None):
if product.stock <= 0:
raise Exception("库存不足")
return True
class BalanceHandler:
def handle(self, user, product, coupon=None):
if not user.is_vip and user.balance < product.price:
raise Exception("余额不足")
return True
# 链条组装
class OrderChain:
def __init__(self):
self.handlers = [
LoginHandler(),
StockHandler(),
BalanceHandler(),
# 随时可以加新的处理者
]
def handle(self, user, product, coupon=None):
for handler in self.handlers:
handler.handle(user, product, coupon)
return Order(user, product)
看到区别了吗?
- 每个处理者只干一件事,符合单一职责
- 加新校验只需要新增一个类,然后加到链里,不用改原有代码
- 不同场景可以组装不同的链:VIP订单链、大促订单链、普通订单链
- 每个处理者可以独立测试
这就是责任链模式的威力。接下来,我们深入了解它。
三、是什么:专业解释、大白话和生活案例
3.1 专业解释
责任链模式(Chain of Responsibility Pattern)是 GoF《设计模式》中定义的23种设计模式之一,属于行为型模式。
GoF 官方定义:
Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it.
翻译成中文:将请求的发送和接收解耦,让多个接收对象都有机会处理这个请求。将这些接收对象串成一条链,并沿着这条链传递请求,直到有对象处理它为止。
核心角色有三个:
| 角色 | 说明 |
|---|---|
| Handler(抽象处理者) | 定义处理请求的接口,持有下一个处理者的引用 |
| ConcreteHandler(具体处理者) | 实现处理逻辑,判断自己能否处理,不能则转发 |
| Client(客户端) | 创建处理者链,发起请求 |

3.2 大白话解释
说人话就是:你有一个请求,不知道谁能处理,那就让它在一条流水线上挨个问,谁能处理谁就处理,处理不了就传给下一个。
就像你去政府部门办事:
- 先到1号窗口,1号窗口说"这个我办不了,你去2号窗口"
- 到了2号窗口,2号窗口说"材料不全,你去3号窗口开证明"
- 到了3号窗口,3号窗口说"这个我能办",然后给你办了
你(请求发送者)不需要知道到底哪个窗口能办,你只需要从第一个窗口开始,让它自己往下传就行。
3.3 生活案例
案例1:公司请假审批
- 请假1天以内:组长审批
- 请假1-3天:经理审批
- 请假3-7天:总监审批
- 请假7天以上:CEO审批
你提交请假申请后,系统先发给组长。组长看了一眼,3天假,超出我的权限了,转给经理。经理看了一眼,5天假,超出我的权限了,转给总监。总监看了一眼,5天,在我权限内,批准。
这就是典型的责任链模式。
案例2:医院看病
- 先到分诊台,护士判断你挂什么科
- 到了科室,医生问诊
- 医生说需要验血,转给检验科
- 检验科出结果,转回医生
- 医生开处方,转给药房
整个过程就是一条责任链,每个环节处理自己的部分,然后传给下一个。
案例3:快递分拣
快递从寄出到送达,要经过:
- 收件网点 → 区域分拣中心 → 城市分拣中心 → 目的地分拣中心 → 派送网点 → 快递员
每个节点都只负责自己那一段,处理完就传给下一个。
四、为什么用:责任链模式解决了什么问题
4.1 核心价值
| 问题 | 责任链模式的解法 |
|---|---|
| 请求发送者和处理者耦合 | 发送者只需要知道链头,不需要知道具体谁处理 |
| 多个 if-else 堆叠 | 每个条件拆成独立处理者,代码清晰 |
| 处理逻辑无法复用 | 处理者可以在不同的链中复用 |
| 流程变化需要改核心代码 | 新增/删除/调整处理者只需要改链的组装 |
| 无法动态调整流程 | 链的顺序和成员可以在运行时动态修改 |
4.2 设计原则的体现
责任链模式完美体现了几个经典设计原则:
- 单一职责原则:每个处理者只负责一件事
- 开闭原则:新增处理者不需要修改原有代码
- 迪米特法则:请求发送者不需要知道处理者的内部结构
- 里氏替换原则:所有处理者实现统一接口,可以互相替换
4.3 什么时候该用
当你遇到以下场景时,就应该考虑责任链模式:
- 一个请求需要经过多个处理步骤,且步骤顺序可能变化
- 不知道哪个处理者能处理请求,需要动态判断
- 需要动态增删处理逻辑
- 多个处理者之间是"或"的关系(找到一个能处理的就行)或者"与"的关系(每个都要处理一遍)
- 代码里有大量 if-else / switch-case,且每个分支逻辑独立
五、它是怎么演进过来的:从 if-else 到中间件
5.1 第一阶段:朴素的 if-else
最早的时候,程序员处理流程化逻辑就是写 if-else。简单直接,但流程一复杂就完蛋。
python
def process(request):
if step1(request):
if step2(request):
if step3(request):
return "success"
return "fail"
5.2 第二阶段:列表循环
有人想到把处理函数放到列表里,然后循环执行。这已经有了责任链的雏形。
python
def process(request):
handlers = [step1, step2, step3]
for handler in handlers:
if not handler(request):
return "fail"
return "success"
优点是可以动态增删 handler,但缺点是每个 handler 无法控制是否继续传递,只能靠返回值判断。
5.3 第三阶段:经典责任链模式
GoF 在1994年的《设计模式》一书中正式定义了责任链模式。每个处理者持有下一个处理者的引用,自己决定是否传递。
python
class Handler:
def __init__(self):
self.next = None
def set_next(self, handler):
self.next = handler
return handler
def handle(self, request):
if self.can_handle(request):
return self.do_handle(request)
elif self.next:
return self.next.handle(request)
return None
5.4 第四阶段:不纯责任链(过滤器/中间件)
实际开发中,纯责任链(一个请求只被一个处理者处理)用得并不多。更常见的是"不纯责任链":每个处理者都处理一部分,然后传给下一个。
这就是 Web 框架中**过滤器(Filter)和中间件(Middleware)**的理论基础。
python
class Middleware:
def __init__(self, next_middleware=None):
self.next = next_middleware
def __call__(self, request):
# 前置处理
response = self.next(request) if self.next else self.get_response(request)
# 后置处理
return response
Django、Flask、FastAPI、Spring MVC 等几乎所有 Web 框架的中间件机制,都是不纯责任链的工程化实现。
5.5 第五阶段:Pipeline / 管道模式
在数据处理、ETL、AI 流水线等场景,责任链进一步演化为 Pipeline 模式。每个处理者接收上一个的输出,处理后传给下一个。
python
class Pipeline:
def __init__(self):
self.steps = []
def add_step(self, step):
self.steps.append(step)
return self
def run(self, data):
for step in self.steps:
data = step(data)
return data
LangChain 的 Chain、Scikit-learn 的 Pipeline、Airflow 的 DAG,本质上都是责任链思想的延伸。
六、怎么用:Python 完整实现
6.1 经典实现(纯责任链)
我们以请假审批为例,实现一个经典的纯责任链模式。
python
from abc import ABC, abstractmethod
from dataclasses import dataclass
@dataclass
class LeaveRequest:
"""请假请求"""
name: str # 请假人
days: int # 请假天数
reason: str # 请假原因
class Handler(ABC):
"""抽象处理者"""
def __init__(self, name: str):
self.name = name
self._next_handler = None
def set_next(self, handler: "Handler") -> "Handler":
"""设置下一个处理者,返回它以便链式调用"""
self._next_handler = handler
return handler
@abstractmethod
def can_handle(self, request: LeaveRequest) -> bool:
"""判断当前处理者能否处理该请求"""
pass
@abstractmethod
def do_handle(self, request: LeaveRequest) -> str:
"""执行处理逻辑"""
pass
def handle(self, request: LeaveRequest) -> str:
"""处理请求:能处理就处理,不能就传给下一个"""
if self.can_handle(request):
return self.do_handle(request)
elif self._next_handler:
print(f"[{self.name}] 超出权限,转交给 [{self._next_handler.name}]")
return self._next_handler.handle(request)
else:
return f"无人能处理该请求:{request.name}的请假申请"
class TeamLeader(Handler):
"""组长:审批1天以内的请假"""
def can_handle(self, request: LeaveRequest) -> bool:
return request.days <= 1
def do_handle(self, request: LeaveRequest) -> str:
return f"[组长 {self.name}] 批准了 {request.name} 的 {request.days} 天请假"
class Manager(Handler):
"""经理:审批1-3天的请假"""
def can_handle(self, request: LeaveRequest) -> bool:
return 1 < request.days <= 3
def do_handle(self, request: LeaveRequest) -> str:
return f"[经理 {self.name}] 批准了 {request.name} 的 {request.days} 天请假"
class Director(Handler):
"""总监:审批3-7天的请假"""
def can_handle(self, request: LeaveRequest) -> bool:
return 3 < request.days <= 7
def do_handle(self, request: LeaveRequest) -> str:
return f"[总监 {self.name}] 批准了 {request.name} 的 {request.days} 天请假"
class CEO(Handler):
"""CEO:审批7天以上的请假"""
def can_handle(self, request: LeaveRequest) -> bool:
return request.days > 7
def do_handle(self, request: LeaveRequest) -> str:
return f"[CEO {self.name}] 批准了 {request.name} 的 {request.days} 天请假"
if __name__ == "__main__":
# 组装责任链
team_leader = TeamLeader("张三")
manager = Manager("李四")
director = Director("王五")
ceo = CEO("赵六")
team_leader.set_next(manager).set_next(director).set_next(ceo)
# 测试不同的请假请求
requests = [
LeaveRequest("小明", 1, "感冒"),
LeaveRequest("小红", 3, "家里有事"),
LeaveRequest("小刚", 5, "旅游"),
LeaveRequest("小丽", 10, "出国留学"),
]
for req in requests:
print(f"\n=== {req.name} 申请请假 {req.days} 天,原因:{req.reason} ===")
result = team_leader.handle(req)
print(result)
运行结果:
text
=== 小明 申请请假 1 天,原因:感冒 ===
[组长 张三] 批准了 小明 的 1 天请假
=== 小红 申请请假 3 天,原因:家里有事 ===
[组长 张三] 超出权限,转交给 [经理 李四]
[经理 李四] 批准了 小红 的 3 天请假
=== 小刚 申请请假 5 天,原因:旅游 ===
[组长 张三] 超出权限,转交给 [经理 李四]
[经理 李四] 超出权限,转交给 [总监 王五]
[总监 王五] 批准了 小刚 的 5 天请假
=== 小丽 申请请假 10 天,原因:出国留学 ===
[组长 张三] 超出权限,转交给 [经理 李四]
[经理 李四] 超出权限,转交给 [总监 王五]
[总监 王五] 超出权限,转交给 [CEO 赵六]
[CEO 赵六] 批准了 小丽 的 10 天请假
6.2 不纯责任链实现(过滤器/中间件风格)
纯责任链是"找到一个能处理的就停",但实际开发中更常用的是不纯责任链:每个处理者都处理一部分,然后传给下一个。

我们以 HTTP 请求过滤器为例:
python
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from typing import Optional
@dataclass
class Request:
"""HTTP请求"""
url: str
method: str
headers: dict = field(default_factory=dict)
body: str = ""
user_id: Optional[int] = None
rate_limit_remaining: int = 100
@dataclass
class Response:
"""HTTP响应"""
status_code: int = 200
body: str = ""
headers: dict = field(default_factory=dict)
class Filter(ABC):
"""抽象过滤器"""
def __init__(self):
self._next_filter = None
def set_next(self, filter_obj: "Filter") -> "Filter":
self._next_filter = filter_obj
return filter_obj
@abstractmethod
def do_filter(self, request: Request) -> Optional[Response]:
"""
过滤逻辑:
- 返回 Response 表示拦截请求,不再继续传递
- 返回 None 表示放行,继续传给下一个过滤器
"""
pass
def filter(self, request: Request) -> Response:
# 执行当前过滤器
response = self.do_filter(request)
if response is not None:
return response
# 放行,传给下一个
if self._next_filter:
return self._next_filter.filter(request)
# 链尾,返回默认响应
return Response(status_code=200, body="OK")
class LoggingFilter(Filter):
"""日志过滤器:记录请求日志"""
def do_filter(self, request: Request) -> Optional[Response]:
print(f"[LOG] {request.method} {request.url}")
return None # 放行
class AuthFilter(Filter):
"""认证过滤器:校验用户身份"""
def do_filter(self, request: Request) -> Optional[Response]:
token = request.headers.get("Authorization")
if not token:
return Response(status_code=401, body="未授权")
# 模拟解析token
request.user_id = 123
print(f"[AUTH] 用户 {request.user_id} 认证通过")
return None # 放行
class RateLimitFilter(Filter):
"""限流过滤器:限制请求频率"""
def do_filter(self, request: Request) -> Optional[Response]:
if request.rate_limit_remaining <= 0:
return Response(status_code=429, body="请求过于频繁")
request.rate_limit_remaining -= 1
print(f"[RATE_LIMIT] 剩余请求次数: {request.rate_limit_remaining}")
return None # 放行
class BlacklistFilter(Filter):
"""黑名单过滤器:拦截黑名单用户"""
BLACKLIST = {1001, 1002} # 模拟黑名单用户ID
def do_filter(self, request: Request) -> Optional[Response]:
if request.user_id in self.BLACKLIST:
return Response(status_code=403, body="用户在黑名单中")
return None # 放行
if __name__ == "__main__":
# 组装过滤链
logging_filter = LoggingFilter()
auth_filter = AuthFilter()
rate_limit_filter = RateLimitFilter()
blacklist_filter = BlacklistFilter()
logging_filter.set_next(auth_filter).set_next(rate_limit_filter).set_next(blacklist_filter)
# 测试正常请求
print("=== 正常请求 ===")
normal_request = Request(
url="/api/user",
method="GET",
headers={"Authorization": "Bearer token123"}
)
response = logging_filter.filter(normal_request)
print(f"响应: {response.status_code} {response.body}\n")
# 测试未授权请求
print("=== 未授权请求 ===")
unauthorized_request = Request(url="/api/user", method="GET")
response = logging_filter.filter(unauthorized_request)
print(f"响应: {response.status_code} {response.body}")
运行结果:
text
=== 正常请求 ===
[LOG] GET /api/user
[AUTH] 用户 123 认证通过
[RATE_LIMIT] 剩余请求次数: 99
响应: 200 OK
=== 未授权请求 ===
[LOG] GET /api/user
响应: 401 未授权
6.3 Pythonic 实现(函数式 + 装饰器)
Python 不需要像 Java 那样写一堆类。利用函数是一等公民的特性,我们可以用更简洁的方式实现责任链。
python
from typing import Callable, List
# 每个处理者就是一个函数,返回 True 表示继续,False 表示中断
def login_check(user, product):
if not user.get("is_login"):
raise Exception("请先登录")
return True
def stock_check(user, product):
if product.get("stock", 0) <= 0:
raise Exception("库存不足")
return True
def balance_check(user, product):
if user.get("balance", 0) < product.get("price", 0):
raise Exception("余额不足")
return True
class Chain:
"""函数式责任链"""
def __init__(self):
self._handlers: List[Callable] = []
def add(self, handler: Callable) -> "Chain":
self._handlers.append(handler)
return self
def execute(self, *args, **kwargs):
for handler in self._handlers:
result = handler(*args, **kwargs)
if result is False: # 返回 False 表示中断
return None
return "全部校验通过"
if __name__ == "__main__":
chain = Chain()
chain.add(login_check).add(stock_check).add(balance_check)
user = {"is_login": True, "balance": 100}
product = {"stock": 10, "price": 50}
result = chain.execute(user, product)
print(result) # 全部校验通过
更高级的玩法:用装饰器自动注册处理者。
python
class OrderValidator:
"""订单校验器:用装饰器自动注册"""
_validators = []
@classmethod
def register(cls, func):
cls._validators.append(func)
return func
@classmethod
def validate(cls, user, product):
for validator in cls._validators:
validator(user, product)
return True
@OrderValidator.register
def check_login(user, product):
if not user.get("is_login"):
raise ValueError("请先登录")
@OrderValidator.register
def check_stock(user, product):
if product.get("stock", 0) <= 0:
raise ValueError("库存不足")
@OrderValidator.register
def check_balance(user, product):
if user.get("balance", 0) < product.get("price", 0):
raise ValueError("余额不足")
if __name__ == "__main__":
user = {"is_login": True, "balance": 100}
product = {"stock": 10, "price": 50}
print(OrderValidator.validate(user, product)) # True
七、企业项目实战:订单审核系统
光说不练假把式。我们来看一个真实的企业级场景:电商订单审核系统。

7.1 业务背景
一个电商平台的订单在支付前需要经过多层审核:
- 基础信息校验:用户是否登录、收货地址是否完整
- 风控校验:是否是恶意用户、是否在黑名单、下单频率是否异常
- 库存校验:商品是否有货、是否超卖
- 价格校验:价格是否被篡改、是否符合促销规则
- 优惠券校验:优惠券是否有效、是否满足使用条件
- 配送校验:收货地址是否在配送范围内
- 发票校验:如果需要发票,发票信息是否完整
不同类型的订单走不同的审核流程:
- 普通订单:全部校验
- VIP订单:跳过余额校验,增加专属客服校验
- 秒杀订单:增加限购校验,跳过优惠券校验
- 预售订单:跳过库存校验(因为还没到货)
7.2 代码实现
python
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from typing import List, Optional, Dict
from enum import Enum
class OrderType(Enum):
NORMAL = "normal" # 普通订单
VIP = "vip" # VIP订单
SECKILL = "seckill" # 秒杀订单
PRESALE = "presale" # 预售订单
@dataclass
class Order:
order_id: str
user_id: int
items: List[Dict]
total_amount: float
address: Dict
coupon_id: Optional[str] = None
need_invoice: bool = False
invoice_info: Optional[Dict] = None
order_type: OrderType = OrderType.NORMAL
is_vip: bool = False
@dataclass
class AuditResult:
passed: bool
reason: str = ""
class AuditHandler(ABC):
"""审核处理者基类"""
def __init__(self, name: str):
self.name = name
self._next = None
def set_next(self, handler: "AuditHandler") -> "AuditHandler":
self._next = handler
return handler
@abstractmethod
def should_skip(self, order: Order) -> bool:
"""判断当前处理者是否需要跳过(针对不同订单类型)"""
pass
@abstractmethod
def audit(self, order: Order) -> AuditResult:
"""执行审核逻辑"""
pass
def handle(self, order: Order) -> AuditResult:
if self.should_skip(order):
print(f"[跳过] {self.name}")
else:
print(f"[审核中] {self.name}")
result = self.audit(order)
if not result.passed:
print(f"[拒绝] {self.name}: {result.reason}")
return result
print(f"[通过] {self.name}")
if self._next:
return self._next.handle(order)
return AuditResult(passed=True, reason="全部审核通过")
class BaseInfoHandler(AuditHandler):
"""基础信息校验"""
def should_skip(self, order: Order) -> bool:
return False # 所有订单都要校验
def audit(self, order: Order) -> AuditResult:
if not order.user_id:
return AuditResult(False, "用户未登录")
if not order.address.get("phone"):
return AuditResult(False, "收货手机号不能为空")
if not order.address.get("detail"):
return AuditResult(False, "收货地址不能为空")
return AuditResult(True)
class RiskControlHandler(AuditHandler):
"""风控校验"""
BLACKLIST_USERS = {1001, 1002, 1003}
def should_skip(self, order: Order) -> bool:
return False # 所有订单都要风控
def audit(self, order: Order) -> AuditResult:
if order.user_id in self.BLACKLIST_USERS:
return AuditResult(False, "用户在黑名单中")
# 模拟查询用户最近下单频率
# recent_orders = query_recent_orders(order.user_id)
# if len(recent_orders) > 10:
# return AuditResult(False, "下单频率异常")
return AuditResult(True)
class StockHandler(AuditHandler):
"""库存校验"""
def should_skip(self, order: Order) -> bool:
# 预售订单跳过库存校验
return order.order_type == OrderType.PRESALE
def audit(self, order: Order) -> AuditResult:
for item in order.items:
# 模拟查询库存
# stock = query_stock(item["product_id"])
stock = 100 # 模拟有货
if stock < item["quantity"]:
return AuditResult(False, f"商品 {item['product_id']} 库存不足")
return AuditResult(True)
class PriceHandler(AuditHandler):
"""价格校验"""
def should_skip(self, order: Order) -> bool:
return False
def audit(self, order: Order) -> AuditResult:
# 模拟重新计算价格,防止被篡改
calculated_amount = sum(item["price"] * item["quantity"] for item in order.items)
if abs(calculated_amount - order.total_amount) > 0.01:
return AuditResult(False, "订单金额异常")
return AuditResult(True)
class CouponHandler(AuditHandler):
"""优惠券校验"""
def should_skip(self, order: Order) -> bool:
# 秒杀订单不支持优惠券
if order.order_type == OrderType.SECKILL:
return True
# 没有使用优惠券
if not order.coupon_id:
return True
return False
def audit(self, order: Order) -> AuditResult:
# 模拟查询优惠券
# coupon = query_coupon(order.coupon_id)
coupon = {"valid": True, "min_amount": 100, "expired": False}
if not coupon["valid"] or coupon["expired"]:
return AuditResult(False, "优惠券无效或已过期")
if order.total_amount < coupon["min_amount"]:
return AuditResult(False, "未达到优惠券使用门槛")
return AuditResult(True)
class DeliveryHandler(AuditHandler):
"""配送校验"""
SUPPORTED_REGIONS = {"北京", "上海", "广州", "深圳", "杭州"}
def should_skip(self, order: Order) -> bool:
return False
def audit(self, order: Order) -> AuditResult:
region = order.address.get("city", "")
if region not in self.SUPPORTED_REGIONS:
return AuditResult(False, f"该地区 {region} 暂不支持配送")
return AuditResult(True)
class InvoiceHandler(AuditHandler):
"""发票校验"""
def should_skip(self, order: Order) -> bool:
return not order.need_invoice
def audit(self, order: Order) -> AuditResult:
if not order.invoice_info:
return AuditResult(False, "发票信息不能为空")
if not order.invoice_info.get("title"):
return AuditResult(False, "发票抬头不能为空")
return AuditResult(True)
class VIPExclusiveHandler(AuditHandler):
"""VIP专属客服校验(仅VIP订单)"""
def should_skip(self, order: Order) -> bool:
return not order.is_vip
def audit(self, order: Order) -> AuditResult:
# 模拟VIP专属校验
print(" VIP专属客服正在为您服务...")
return AuditResult(True)
class SeckillLimitHandler(AuditHandler):
"""秒杀限购校验(仅秒杀订单)"""
def should_skip(self, order: Order) -> bool:
return order.order_type != OrderType.SECKILL
def audit(self, order: Order) -> AuditResult:
# 模拟查询用户今日秒杀订单数
# today_seckill_count = query_today_seckill_count(order.user_id)
today_seckill_count = 1
if today_seckill_count >= 3:
return AuditResult(False, "秒杀商品每人限购3件")
return AuditResult(True)
class OrderAuditChain:
"""订单审核链工厂:根据订单类型组装不同的审核链"""
@staticmethod
def create_chain(order_type: OrderType) -> AuditHandler:
# 基础审核链
base_info = BaseInfoHandler("基础信息校验")
risk_control = RiskControlHandler("风控校验")
stock = StockHandler("库存校验")
price = PriceHandler("价格校验")
coupon = CouponHandler("优惠券校验")
delivery = DeliveryHandler("配送校验")
invoice = InvoiceHandler("发票校验")
# 组装基础链
chain = base_info
last = base_info.set_next(risk_control)
last = last.set_next(stock)
last = last.set_next(price)
last = last.set_next(coupon)
last = last.set_next(delivery)
last = last.set_next(invoice)
# 根据订单类型添加特殊审核
if order_type == OrderType.VIP:
vip_handler = VIPExclusiveHandler("VIP专属校验")
last.set_next(vip_handler)
elif order_type == OrderType.SECKILL:
seckill_handler = SeckillLimitHandler("秒杀限购校验")
last.set_next(seckill_handler)
return chain
def audit_order(order: Order) -> AuditResult:
"""审核订单入口"""
print(f"\n{'='*50}")
print(f"开始审核订单: {order.order_id} (类型: {order.order_type.value})")
print(f"{'='*50}")
chain = OrderAuditChain.create_chain(order.order_type)
result = chain.handle(order)
print(f"\n审核结果: {'通过' if result.passed else '拒绝'} - {result.reason}")
return result
if __name__ == "__main__":
# 测试普通订单
normal_order = Order(
order_id="ORD20240101001",
user_id=12345,
items=[{"product_id": "P001", "price": 99.0, "quantity": 2}],
total_amount=198.0,
address={"phone": "13800138000", "detail": "xxx街道xxx号", "city": "北京"},
coupon_id="C001",
need_invoice=True,
invoice_info={"title": "某某公司"},
order_type=OrderType.NORMAL
)
audit_order(normal_order)
# 测试秒杀订单
seckill_order = Order(
order_id="ORD20240101002",
user_id=12345,
items=[{"product_id": "P002", "price": 9.9, "quantity": 1}],
total_amount=9.9,
address={"phone": "13800138000", "detail": "xxx街道xxx号", "city": "上海"},
order_type=OrderType.SECKILL
)
audit_order(seckill_order)
# 测试VIP订单
vip_order = Order(
order_id="ORD20240101003",
user_id=12345,
items=[{"product_id": "P003", "price": 999.0, "quantity": 1}],
total_amount=999.0,
address={"phone": "13800138000", "detail": "xxx街道xxx号", "city": "深圳"},
is_vip=True,
order_type=OrderType.VIP
)
audit_order(vip_order)
7.3 这个设计好在哪
- 每种订单类型的审核流程独立配置:普通订单、VIP订单、秒杀订单、预售订单各走各的链,互不干扰
- 新增审核项只需要加一个类 :比如要加一个"积分抵扣校验",新增一个
PointHandler类,然后在对应链里加上就行 - 每个审核逻辑可以独立测试 :测库存校验就只测
StockHandler,不需要构造整个订单流程 - 审核顺序可以灵活调整:比如要把风控校验放到最前面,只需要改链的组装顺序
- 可以动态配置:甚至可以把审核链的配置存到数据库/配置中心,运营人员可以在后台调整审核流程,不需要发版
八、竞品对比:责任链 vs 装饰器 vs 策略 vs 观察者
这几个模式经常被搞混,我们来做一个全面的对比。

8.1 核心区别对比表
| 对比维度 | 责任链模式 | 装饰器模式 | 策略模式 | 观察者模式 |
|---|---|---|---|---|
| 核心意图 | 请求沿链传递,找到能处理的对象 | 动态增强对象功能 | 在多种算法中选一个执行 | 一对多通知,状态变化广播 |
| 对象关系 | 处理者持有下一个处理者引用 | 装饰者持有被装饰对象 | Context持有策略引用 | Subject持有多个Observer |
| 请求处理方式 | 一个或多个处理者依次处理 | 所有装饰器都参与,层层增强 | 只有一个策略被选中执行 | 所有观察者都收到通知 |
| 是否可中断 | 可以,处理者可以终止传递 | 不可以,必须层层传递 | 不涉及 | 不可以,所有观察者都收到 |
| 关注点 | 流程、步骤、传递 | 功能增强、包装 | 算法选择、替换 | 事件通知、解耦 |
| 典型场景 | 审批流程、过滤器链、中间件 | IO流包装、AOP、缓存装饰 | 支付方式选择、排序算法 | 消息队列、事件总线、UI更新 |
| 客户端感知 | 客户端只知道链头 | 客户端知道最外层装饰器 | 客户端知道当前策略 | 客户端只知道Subject |
| 运行时动态性 | 可以动态增删改链的成员 | 可以动态嵌套装饰器 | 可以动态切换策略 | 可以动态增删观察者 |
8.2 用生活案例理解区别
责任链模式:就像医院看病流程。你从分诊台开始,分诊台说你去内科,内科医生说你去验血,验完血回来医生给你开药。每个环节处理自己的部分,然后传给下一个。
装饰器模式:就像买咖啡。你买一杯基础咖啡,然后加奶(奶装饰器),加糖(糖装饰器),加奶油(奶油装饰器)。每加一样,咖啡的功能(味道、价格)就增强一点,但本质还是咖啡。
策略模式:就像出行方式选择。你要去一个地方,可以选开车、坐地铁、骑自行车、打车。你根据距离、天气、时间等因素选一种方式,选了就只用这一种。
观察者模式:就像订阅报纸。你(观察者)订阅了报社(被观察者),报社一有新报纸就给所有订阅者送一份。你不需要每天去问有没有新报纸,报社主动通知你。
8.3 代码结构对比
python
# ===== 责任链模式 =====
class Handler:
def __init__(self):
self.next = None # 持有下一个处理者
def handle(self, request):
if self.can_handle(request):
return self.do_handle(request)
return self.next.handle(request) # 传给下一个
# ===== 装饰器模式 =====
class Decorator:
def __init__(self, component):
self.component = component # 持有被装饰对象
def operation(self):
# 前置增强
result = self.component.operation() # 调用被装饰对象
# 后置增强
return result
# ===== 策略模式 =====
class Context:
def __init__(self, strategy):
self.strategy = strategy # 持有当前策略
def execute(self, data):
return self.strategy.execute(data) # 只调用选中的策略
# ===== 观察者模式 =====
class Subject:
def __init__(self):
self.observers = [] # 持有多个观察者
def notify(self, event):
for observer in self.observers:
observer.update(event) # 通知所有观察者
8.4 怎么选
| 你的需求 | 应该用 |
|---|---|
| 请求需要经过多个步骤,步骤顺序可能变 | 责任链 |
| 要给一个对象动态加功能,且功能可以叠加 | 装饰器 |
| 有多种算法/方案,运行时选一个 | 策略 |
| 一个对象变化要通知多个其他对象 | 观察者 |
| 既要流程化处理,又要每步都增强 | 责任链 + 装饰器组合 |
| 流程中某个步骤有多种实现 | 责任链 + 策略组合 |
九、常用场景总结
9.1 Web开发场景
| 场景 | 说明 | 典型框架 |
|---|---|---|
| 请求过滤器链 | 日志、编码、安全、XSS过滤等 | Servlet Filter、Django Middleware |
| 拦截器链 | 权限校验、登录验证、日志记录 | Spring MVC Interceptor、FastAPI Depends |
| 网关过滤器 | 鉴权、限流、熔断、路由 | Spring Cloud Gateway、Kong、Nginx |
| 中间件管道 | 请求处理流水线 | Express、Koa、FastAPI |
9.2 业务系统场景
| 场景 | 说明 |
|---|---|
| 审批流程 | 请假、报销、采购、合同审批 |
| 订单审核 | 风控、库存、价格、优惠券、配送校验 |
| 内容审核 | 敏感词、图片、视频、人工审核 |
| 支付流程 | 支付方式选择、风控、扣款、回调 |
| 工单处理 | 一线客服 → 二线技术 → 三线专家 |
| 异常处理 | 异常类型匹配,找到对应的处理器 |
9.3 数据处理场景
| 场景 | 说明 |
|---|---|
| ETL流水线 | 抽取 → 清洗 → 转换 → 加载 |
| 数据校验 | 格式校验 → 完整性校验 → 业务规则校验 |
| AI推理流水线 | 预处理 → 模型推理 → 后处理 → 输出 |
| 日志处理 | 采集 → 过滤 → 格式化 → 输出 |
9.4 框架源码中的应用
| 框架/库 | 应用 |
|---|---|
| Spring Security | 过滤器链实现认证授权 |
| MyBatis | 插件拦截器链(分页、SQL监控等) |
| Netty | ChannelPipeline 责任链处理IO事件 |
| Dubbo | Filter链实现RPC调用拦截 |
| Django | Middleware中间件链 |
| FastAPI | Depends依赖注入链 |
| LangChain | Chain链式调用 |
| Scikit-learn | Pipeline数据处理管道 |
十、面试官高频面试题
Q1:什么是责任链模式?它的核心角色有哪些?
参考答案:
责任链模式是一种行为型设计模式,它将请求的发送者和接收者解耦,让多个对象都有机会处理请求。这些对象被串成一条链,请求沿着链传递,直到有对象处理它为止。
核心角色有三个:
- Handler(抽象处理者):定义处理请求的接口,持有下一个处理者的引用
- ConcreteHandler(具体处理者):实现具体的处理逻辑,判断自己能否处理,不能则转发给下一个
- Client(客户端):创建责任链,向链头发起请求
Q2:纯责任链和不纯责任链有什么区别?
参考答案:
| 维度 | 纯责任链 | 不纯责任链 |
|---|---|---|
| 处理方式 | 一个请求只能被一个处理者处理 | 一个请求可以被多个处理者依次处理 |
| 传递时机 | 处理不了才传递 | 处理完自己的部分后继续传递 |
| 链中断 | 处理后自动停止 | 需要显式中断(返回响应/抛异常) |
| 典型场景 | 请假审批、报销审批 | 过滤器链、中间件、拦截器 |
纯责任链是GoF定义的标准形式,但实际开发中不纯责任链用得更多,比如Web框架的过滤器和中间件都是不纯责任链的实现。
Q3:责任链模式和装饰器模式有什么区别?
参考答案:
这两个模式的类结构很相似,都用到了对象组合,但意图完全不同:
-
核心目的不同:
- 责任链:请求在链上传递,找到能处理的对象(或每个都处理一部分),重点是"流程"和"传递"
- 装饰器:动态增强对象的功能,重点是"增强"和"包装"
-
处理方式不同:
- 责任链:处理者可以决定是否继续传递,链可以中断
- 装饰器:所有装饰器都必须参与,层层调用,不能跳过
-
关系不同:
- 责任链:处理者之间是"或"的关系(找到一个能处理的就行)或"接力"关系
- 装饰器:装饰器之间是"且"的关系(所有装饰器都要执行)
-
典型场景不同:
- 责任链:审批流程、过滤器链、中间件
- 装饰器:IO流包装、AOP、缓存装饰器
Q4:责任链模式有什么优缺点?
参考答案:
优点:
- 降低耦合度:请求发送者不需要知道具体哪个处理者会处理,处理者也不需要知道请求的全貌
- 简化对象:每个处理者只需要持有下一个处理者的引用,不需要知道整个链的结构
- 增强灵活性:可以动态地增删处理者、调整处理顺序
- 符合开闭原则:新增处理者不需要修改原有代码
- 职责分离:每个处理者只关注自己的处理逻辑,符合单一职责
缺点:
- 不能保证请求一定被处理:如果链上没有合适的处理者,请求会到达链尾而不被处理
- 性能影响:请求可能需要遍历整个链才能找到处理者,链太长时性能有损耗
- 调试困难:请求在链上传递,出错时不容易定位是哪个处理者的问题
- 可能造成循环调用:如果链的配置有误,可能出现循环引用导致死循环
- 不适合处理者之间有复杂依赖的场景:如果处理者之间有数据依赖,责任链会变得复杂
Q5:责任链模式在实际项目中有哪些应用?
参考答案:
-
Web框架的过滤器/中间件:
- Java Servlet Filter、Spring MVC Interceptor
- Django Middleware、FastAPI Depends
- Express/Koa 中间件
-
网关层:
- Spring Cloud Gateway 过滤器链
- Nginx 请求处理阶段
- Kong 插件链
-
安全框架:
- Spring Security 过滤器链(认证、授权、CSRF等)
- Shiro 过滤器链
-
业务系统:
- 审批流程(请假、报销、采购)
- 订单审核(风控、库存、价格、优惠券)
- 内容审核(敏感词、图片、人工审核)
- 支付流程
-
数据处理:
- ETL 流水线
- 日志处理链
- AI 推理流水线
-
框架源码:
- MyBatis 插件拦截器链
- Netty ChannelPipeline
- Dubbo Filter 链
- LangChain Chain
Q6:如何实现一个通用的责任链框架?
参考答案:
一个通用的责任链框架应该包含以下要素:
python
from abc import ABC, abstractmethod
from typing import List, Any
class Context:
"""请求上下文,在处理者之间传递数据"""
def __init__(self):
self._data = {}
self._stopped = False
def set(self, key, value):
self._data[key] = value
def get(self, key, default=None):
return self._data.get(key, default)
def stop(self):
self._stopped = True
@property
def is_stopped(self):
return self._stopped
class Handler(ABC):
"""处理者接口"""
@abstractmethod
def handle(self, context: Context) -> None:
pass
class Chain:
"""责任链"""
def __init__(self):
self._handlers: List[Handler] = []
def add_handler(self, handler: Handler) -> "Chain":
self._handlers.append(handler)
return self
def execute(self, context: Context) -> Context:
for handler in self._handlers:
if context.is_stopped:
break
handler.handle(context)
return context
这个框架的特点:
- 使用 Context 对象在处理者之间传递数据,避免参数爆炸
- 处理者可以通过
context.stop()终止链的执行 - 支持链式调用添加处理者
- 处理者之间完全解耦,通过 Context 共享数据
Q7:责任链模式和策略模式怎么选?
参考答案:
选择依据主要看你的需求:
| 需求特征 | 选责任链 | 选策略 |
|---|---|---|
| 处理步骤 | 多个步骤依次执行 | 只有一个步骤 |
| 处理者数量 | 多个处理者都可能参与 | 只选一个处理者 |
| 流程变化 | 步骤的顺序和数量会变 | 算法的选择会变 |
| 客户端感知 | 客户端只知道链头 | 客户端知道所有策略 |
| 典型问题 | "这个请求要经过哪些步骤?" | "这个请求用哪种算法?" |
简单来说:
- 如果你的问题是"一个请求要经过A→B→C→D多个步骤",用责任链
- 如果你的问题是"一个请求可以用算法1、算法2、算法3,选一个",用策略
- 如果既有流程又有多种算法,可以组合使用:责任链的某个节点用策略模式
Q8:责任链模式会有性能问题吗?怎么优化?
参考答案:
责任链模式的性能问题主要来自两个方面:
- 链太长导致的遍历开销:如果链上有几十上百个处理者,每个请求都要遍历一遍,会有性能损耗
- 处理者之间的重复计算:多个处理者可能查询相同的数据,造成重复IO
优化方案:
- 合理控制链的长度:不要把无关的处理者都塞到一条链里,可以拆成多条链
- 提前终止:能在前面处理的就不要放到后面,比如认证失败就直接返回,不要继续走后面的逻辑
- 上下文缓存:用 Context 对象缓存中间结果,避免重复查询。比如用户信息在第一个处理者查了,后面的处理者直接从 Context 取
- 并行处理:如果处理者之间没有依赖,可以用并行执行代替串行。比如多个校验逻辑可以并行执行,用 CompletableFuture 或 asyncio.gather
- 热点处理者前置:把最可能拦截请求的处理者放到前面,尽早终止链
- 链的预热和缓存:如果链的构建成本高,可以在启动时构建好并缓存,不要每次请求都重新构建
十一、总结
责任链模式是一个看起来简单但非常实用的设计模式。它的核心思想就是:把复杂的流程拆成独立的步骤,然后串成一条链,让请求自己在链上流动。
回顾一下本文的要点:
- 是什么:行为型模式,多个处理者串成链,请求沿链传递
- 为什么用:解耦发送者和接收者,替代 if-else,灵活扩展
- 怎么演进:if-else → 列表循环 → 经典责任链 → 不纯责任链(中间件)→ Pipeline
- 怎么用:纯责任链(审批)、不纯责任链(过滤器)、Pythonic实现(函数式)
- 企业实战:订单审核系统,多种订单类型灵活组装审核链
- 竞品对比:与装饰器、策略、观察者的核心区别
- 常用场景:Web中间件、审批流程、数据处理、框架源码
- 面试题:8道高频面试题及参考答案
记住一句话:当你的代码里出现了超过3层的 if-else,或者一个函数干了超过3件事,就该想想是不是可以用责任链模式重构了。
设计模式不是银弹,不要为了用模式而用模式。但当你真正理解了它的思想,你会发现它能让你的代码更优雅、更易维护、更易扩展。
转载声明:本文为原创文章,如需转载,请联系作者获得授权,并注明出处。