04 · 行为型模式(Behavioral Patterns)

📅 预计 60 分钟 | ⭐ = 重要知识点 | 📌 中英术语见文末


4.0 先给直觉:对象之间怎么"打交道"

结构型模式管"怎么搭",行为型模式管"对象之间怎么协作":谁通知谁(观察者)、算法怎么互换(策略)、流程骨架怎么定(模板方法)、请求怎么传递(责任链)、行为怎么随状态变(状态)。

五者的分工:

  • 观察者:一对多,状态变了广播通知。
  • 策略:算法可以随时换。
  • 模板方法:骨架固定,细节延后。
  • 责任链:请求沿链传递,谁管谁接。
  • 状态:行为随内部状态切换。

4.1 观察者模式(Observer)⭐

订阅-推送

观察者模式建立一对多的依赖:一个对象(主题)状态变化,所有关注它的对象(观察者)自动收到通知。像公众号:你关注后,作者一发文,你的消息列表自动多一条,作者不需要知道都有谁关注。

class Subject:
    def __init__(self):
        self.observers = []

    def attach(self, o):      self.observers.append(o)
    def notify(self):
        for o in self.observers:     # 广播通知所有观察者
            o.update(self)

class Stock(Subject):                 # 主题:股票价格
    def set_price(self, p):
        self.price = p
        self.notify()

class Trader:                         # 观察者
    def update(self, stock):
        print(f"价格变了: {stock.price}")

用途:UI 里模型变化自动刷新视图、消息推送、事件总线。它把"数据"和"依赖数据的部分"解耦——新增一个观察者,主题代码不用改。

⚠️ 常见错误

  1. 观察者之间隐式依赖执行顺序:A 观察者假设 B 观察者先执行,顺序一乱就出 bug。更新顺序不应被依赖。
  2. 忘记移除观察者,主题长期持有不再需要的对象引用,造成内存泄漏。

4.2 策略模式(Strategy)⭐

算法可以互换

策略模式把一组可互相替换的算法各自封装成类,运行时自由切换。像出行方式:同一段路,可以打车、坐地铁、骑自行车——目的地一样,算法不同,随时换。

class TravelStrategy:
    def time(self, dist): ...

class Walk(TravelStrategy):
    def time(self, dist): return dist / 4        # 时速 4 km

class Taxi(TravelStrategy):
    def time(self, dist): return dist / 40       # 时速 40 km

class Trip:
    def __init__(self, strategy):
        self.strategy = strategy

    def set_strategy(self, s):      self.strategy = s   # 运行时换算法
    def estimate(self, dist):
        return self.strategy.time(dist)

好处:把 if-else 分支换成对象组合,符合开闭原则——新增一种出行方式,只需新增一个策略类,主流程不用改。

⚠️ 常见错误

  1. 策略类只有一个、也不存在多种算法时硬拆成策略模式,属于过度设计。
  2. 多个策略共享大量内部状态时,硬抽成独立类反而别扭,要考虑状态放在哪里更合理。

4.3 模板方法模式(Template Method)⭐

骨架固定,细节延后

模板方法把算法的骨架在父类里定义好,把一些步骤延迟到子类实现。像做菜的标准流程:备料 → 下锅 → 调味 → 出锅,流程固定,但不同菜在"下锅"这一步的做法不同。

class Meal:                          # 模板方法定义流程
    def cook(self):                  # 骨架:步骤顺序固定
        self.prepare()
        self.main_step()             # 抽象步骤,交给子类
        self.season()
        self.serve()

    def prepare(self):  print("备料")
    def main_step(self): raise NotImplementedError   # 子类实现
    def serve(self):    print("装盘")

class FriedRice(Meal):
    def main_step(self): print("大火炒饭")

与策略模式的对比:模板方法靠继承定骨架(父类定流程、子类填步骤),策略靠组合换整块算法(把算法整体封装成对象)。模板方法在"流程不变、个别步骤多变"时最合适。

⚠️ 常见错误

  1. 骨架里步骤有先后依赖(必须先备料再下锅),子类重写时打乱顺序会破坏算法正确性——关键顺序应放在不可重写的骨架方法里。
  2. 父类的"钩子方法"(可选扩展点)过多,子类被迫实现一堆不关心的步骤。

4.4 责任链模式(Chain of Responsibility)⭐

层层传递,直到有人接

责任链把请求沿着一条链条传递,每个节点决定"自己处理"还是"传给下一个"。像请假审批:一天假组长批,三天假经理批,一周假总监批——申请从组长一路往上,谁在权限内谁处理。

class Handler:
    def __init__(self): self.next = None
    def set_next(self, h): self.next = h; return h    # 链式组链

    def handle(self, days):
        if self.can_handle(days):
            self.do_handle(days)
        elif self.next:
            self.next.handle(days)                     # 传下去
        else:
            print("无人有权处理")

class TeamLead(Handler):
    def can_handle(self, d): return d <= 1
    def do_handle(self, d):  print(f"组长批 {d} 天")

class Manager(Handler):
    def can_handle(self, d): return d <= 3
    def do_handle(self, d):  print(f"经理批 {d} 天")

用途:中间件、日志级别过滤、表单校验链。好处是请求的发送者不需要知道谁会处理,只把请求扔进链里即可。

⚠️ 常见错误

  1. 链太长、节点逻辑重叠,请求最终被谁处理难以定位,调试成本高。
  2. 请求沿链传到最后没人接,结果"静默丢失"——应设计兜底处理(如记录日志、抛异常),避免无声失败。

4.5 状态模式(State)⭐

行为跟随状态变化

状态模式让对象在内部状态改变时改变自身行为,看起来像换了类。像红绿灯:同样是"看到信号",红灯停、绿灯行——同一个动作因状态而异。

class State:
    def show(self): ...
    def next(self, light): ...

class Red(State):
    def show(self): print("红灯停")
    def next(self, light):
        light.state = Green()          # 状态切换

class Green(State):
    def show(self): print("绿灯行")
    def next(self, light):
        light.state = Red()

class TrafficLight:
    def __init__(self): self.state = Red()
    def show(self):     self.state.show(self)
    def tick(self):     self.state.next(self)

比起一大堆 if-else 判断当前状态,状态模式把"每种状态下该做什么"分散到各自的状态类中,新增状态只需新增一个类,符合开闭原则。

⚠️ 常见错误

  1. 状态对象被多个对象共享,又带有可变数据时,要注意并发环境下的状态污染。
  2. 状态转换逻辑分散在各个状态类里,转换条件复杂时反而难以追踪整体流程——必要时用一张转换表集中说明。

📌 术语对照(中英)

中文 English
观察者 Observer
策略 Strategy
模板方法 Template Method
责任链 Chain of Responsibility
状态 State

🎯 复习清单

  • 五种行为型模式各解决什么"协作问题",分别用一个生活例子说明。
  • 策略 vs 模板方法的本质区别(组合换整块算法 vs 继承定骨架)。
  • 观察者模式的一对多依赖与解耦价值,以及内存泄漏风险。
  • 责任链的传递机制与兜底处理。
  • 状态模式如何把分支判断拆成状态类。