14、Python - 责任链模式

14、Python - 责任链模式


文章目录

一、痛点场景:你的代码是不是也被 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)

这就是典型的"面条式代码"。问题在哪?

  1. 一个函数干了所有事:登录校验、库存校验、余额校验、风控校验全挤在一起,违反单一职责原则
  2. 改一个需求要动整个函数:加一个校验就要在函数中间插代码,容易引入bug
  3. 无法灵活组合:VIP用户要跳过余额校验,大促要加限购,你只能写更多 if-else
  4. 无法复用:这些校验逻辑在退款、换货流程里也要用,但你只能复制粘贴
  5. 测试困难:要测一个校验逻辑,必须把整个函数跑起来

这不是你一个人的问题,这是所有流程化业务的通病。而责任链模式,就是来解决这个问题的。


二、痛点的解决方案:把 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 设计原则的体现

责任链模式完美体现了几个经典设计原则:

  1. 单一职责原则:每个处理者只负责一件事
  2. 开闭原则:新增处理者不需要修改原有代码
  3. 迪米特法则:请求发送者不需要知道处理者的内部结构
  4. 里氏替换原则:所有处理者实现统一接口,可以互相替换

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 业务背景

一个电商平台的订单在支付前需要经过多层审核:

  1. 基础信息校验:用户是否登录、收货地址是否完整
  2. 风控校验:是否是恶意用户、是否在黑名单、下单频率是否异常
  3. 库存校验:商品是否有货、是否超卖
  4. 价格校验:价格是否被篡改、是否符合促销规则
  5. 优惠券校验:优惠券是否有效、是否满足使用条件
  6. 配送校验:收货地址是否在配送范围内
  7. 发票校验:如果需要发票,发票信息是否完整

不同类型的订单走不同的审核流程:

  • 普通订单:全部校验
  • 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 这个设计好在哪

  1. 每种订单类型的审核流程独立配置:普通订单、VIP订单、秒杀订单、预售订单各走各的链,互不干扰
  2. 新增审核项只需要加一个类 :比如要加一个"积分抵扣校验",新增一个 PointHandler 类,然后在对应链里加上就行
  3. 每个审核逻辑可以独立测试 :测库存校验就只测 StockHandler,不需要构造整个订单流程
  4. 审核顺序可以灵活调整:比如要把风控校验放到最前面,只需要改链的组装顺序
  5. 可以动态配置:甚至可以把审核链的配置存到数据库/配置中心,运营人员可以在后台调整审核流程,不需要发版

八、竞品对比:责任链 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:什么是责任链模式?它的核心角色有哪些?

参考答案:

责任链模式是一种行为型设计模式,它将请求的发送者和接收者解耦,让多个对象都有机会处理请求。这些对象被串成一条链,请求沿着链传递,直到有对象处理它为止。

核心角色有三个:

  1. Handler(抽象处理者):定义处理请求的接口,持有下一个处理者的引用
  2. ConcreteHandler(具体处理者):实现具体的处理逻辑,判断自己能否处理,不能则转发给下一个
  3. Client(客户端):创建责任链,向链头发起请求

Q2:纯责任链和不纯责任链有什么区别?

参考答案:

维度 纯责任链 不纯责任链
处理方式 一个请求只能被一个处理者处理 一个请求可以被多个处理者依次处理
传递时机 处理不了才传递 处理完自己的部分后继续传递
链中断 处理后自动停止 需要显式中断(返回响应/抛异常)
典型场景 请假审批、报销审批 过滤器链、中间件、拦截器

纯责任链是GoF定义的标准形式,但实际开发中不纯责任链用得更多,比如Web框架的过滤器和中间件都是不纯责任链的实现。

Q3:责任链模式和装饰器模式有什么区别?

参考答案:

这两个模式的类结构很相似,都用到了对象组合,但意图完全不同:

  1. 核心目的不同

    • 责任链:请求在链上传递,找到能处理的对象(或每个都处理一部分),重点是"流程"和"传递"
    • 装饰器:动态增强对象的功能,重点是"增强"和"包装"
  2. 处理方式不同

    • 责任链:处理者可以决定是否继续传递,链可以中断
    • 装饰器:所有装饰器都必须参与,层层调用,不能跳过
  3. 关系不同

    • 责任链:处理者之间是"或"的关系(找到一个能处理的就行)或"接力"关系
    • 装饰器:装饰器之间是"且"的关系(所有装饰器都要执行)
  4. 典型场景不同

    • 责任链:审批流程、过滤器链、中间件
    • 装饰器:IO流包装、AOP、缓存装饰器

Q4:责任链模式有什么优缺点?

参考答案:

优点:

  1. 降低耦合度:请求发送者不需要知道具体哪个处理者会处理,处理者也不需要知道请求的全貌
  2. 简化对象:每个处理者只需要持有下一个处理者的引用,不需要知道整个链的结构
  3. 增强灵活性:可以动态地增删处理者、调整处理顺序
  4. 符合开闭原则:新增处理者不需要修改原有代码
  5. 职责分离:每个处理者只关注自己的处理逻辑,符合单一职责

缺点:

  1. 不能保证请求一定被处理:如果链上没有合适的处理者,请求会到达链尾而不被处理
  2. 性能影响:请求可能需要遍历整个链才能找到处理者,链太长时性能有损耗
  3. 调试困难:请求在链上传递,出错时不容易定位是哪个处理者的问题
  4. 可能造成循环调用:如果链的配置有误,可能出现循环引用导致死循环
  5. 不适合处理者之间有复杂依赖的场景:如果处理者之间有数据依赖,责任链会变得复杂

Q5:责任链模式在实际项目中有哪些应用?

参考答案:

  1. Web框架的过滤器/中间件

    • Java Servlet Filter、Spring MVC Interceptor
    • Django Middleware、FastAPI Depends
    • Express/Koa 中间件
  2. 网关层

    • Spring Cloud Gateway 过滤器链
    • Nginx 请求处理阶段
    • Kong 插件链
  3. 安全框架

    • Spring Security 过滤器链(认证、授权、CSRF等)
    • Shiro 过滤器链
  4. 业务系统

    • 审批流程(请假、报销、采购)
    • 订单审核(风控、库存、价格、优惠券)
    • 内容审核(敏感词、图片、人工审核)
    • 支付流程
  5. 数据处理

    • ETL 流水线
    • 日志处理链
    • AI 推理流水线
  6. 框架源码

    • 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

这个框架的特点:

  1. 使用 Context 对象在处理者之间传递数据,避免参数爆炸
  2. 处理者可以通过 context.stop() 终止链的执行
  3. 支持链式调用添加处理者
  4. 处理者之间完全解耦,通过 Context 共享数据

Q7:责任链模式和策略模式怎么选?

参考答案:

选择依据主要看你的需求:

需求特征 选责任链 选策略
处理步骤 多个步骤依次执行 只有一个步骤
处理者数量 多个处理者都可能参与 只选一个处理者
流程变化 步骤的顺序和数量会变 算法的选择会变
客户端感知 客户端只知道链头 客户端知道所有策略
典型问题 "这个请求要经过哪些步骤?" "这个请求用哪种算法?"

简单来说:

  • 如果你的问题是"一个请求要经过A→B→C→D多个步骤",用责任链
  • 如果你的问题是"一个请求可以用算法1、算法2、算法3,选一个",用策略
  • 如果既有流程又有多种算法,可以组合使用:责任链的某个节点用策略模式

Q8:责任链模式会有性能问题吗?怎么优化?

参考答案:

责任链模式的性能问题主要来自两个方面:

  1. 链太长导致的遍历开销:如果链上有几十上百个处理者,每个请求都要遍历一遍,会有性能损耗
  2. 处理者之间的重复计算:多个处理者可能查询相同的数据,造成重复IO

优化方案:

  1. 合理控制链的长度:不要把无关的处理者都塞到一条链里,可以拆成多条链
  2. 提前终止:能在前面处理的就不要放到后面,比如认证失败就直接返回,不要继续走后面的逻辑
  3. 上下文缓存:用 Context 对象缓存中间结果,避免重复查询。比如用户信息在第一个处理者查了,后面的处理者直接从 Context 取
  4. 并行处理:如果处理者之间没有依赖,可以用并行执行代替串行。比如多个校验逻辑可以并行执行,用 CompletableFuture 或 asyncio.gather
  5. 热点处理者前置:把最可能拦截请求的处理者放到前面,尽早终止链
  6. 链的预热和缓存:如果链的构建成本高,可以在启动时构建好并缓存,不要每次请求都重新构建

十一、总结

责任链模式是一个看起来简单但非常实用的设计模式。它的核心思想就是:把复杂的流程拆成独立的步骤,然后串成一条链,让请求自己在链上流动。

回顾一下本文的要点:

  1. 是什么:行为型模式,多个处理者串成链,请求沿链传递
  2. 为什么用:解耦发送者和接收者,替代 if-else,灵活扩展
  3. 怎么演进:if-else → 列表循环 → 经典责任链 → 不纯责任链(中间件)→ Pipeline
  4. 怎么用:纯责任链(审批)、不纯责任链(过滤器)、Pythonic实现(函数式)
  5. 企业实战:订单审核系统,多种订单类型灵活组装审核链
  6. 竞品对比:与装饰器、策略、观察者的核心区别
  7. 常用场景:Web中间件、审批流程、数据处理、框架源码
  8. 面试题:8道高频面试题及参考答案

记住一句话:当你的代码里出现了超过3层的 if-else,或者一个函数干了超过3件事,就该想想是不是可以用责任链模式重构了。

设计模式不是银弹,不要为了用模式而用模式。但当你真正理解了它的思想,你会发现它能让你的代码更优雅、更易维护、更易扩展。


转载声明:本文为原创文章,如需转载,请联系作者获得授权,并注明出处。

相关推荐
俊哥V11 分钟前
每日 AI 研究简报 · 2026-08-30
人工智能·ai
番茄不是西红柿kk14 分钟前
GLM-5.3-Flash 20分钟复刻《我的世界》实录
人工智能·ai·aigc·agent·我的世界
霸道流氓气质19 分钟前
Tabbit AI 原生浏览器 — 完全技术参考手册
人工智能
Python大数据分析@19 分钟前
推荐一个生成“结构化”html网页的SKILL
人工智能
阿里云大数据AI技术20 分钟前
DataWorks Data Agent 实战课堂(六):数据集成定时任务巡检
人工智能·agent
张人玉20 分钟前
基于 Python 机器学习 与 PyQt5 桌面框架开发的可视化分析系统——基于大数据的网上购物消费行为分析可视化大屏
python·qt·机器学习·echarts·three.js
吴声子夜歌25 分钟前
Java——基本类型
java·开发语言
一位正在转型AI全栈的前端工程师29 分钟前
AI 全栈学习之旅 -Week 5:RAG 知识库问答系统:从零到生产级部署的全栈实践总结
前端·python
neocheng_52229 分钟前
不绑定厂商的AI认证有哪些特质?通用AI技能等级认证适配哪类人群?
人工智能