SD-04 行为型模式
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 里模型变化自动刷新视图、消息推送、事件总线。它把"数据"和"依赖数据的部分"解耦——新增一个观察者,主题代码不用改。
⚠️ 常见错误
- 观察者之间隐式依赖执行顺序:A 观察者假设 B 观察者先执行,顺序一乱就出 bug。更新顺序不应被依赖。
- 忘记移除观察者,主题长期持有不再需要的对象引用,造成内存泄漏。
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 分支换成对象组合,符合开闭原则——新增一种出行方式,只需新增一个策略类,主流程不用改。
⚠️ 常见错误
- 策略类只有一个、也不存在多种算法时硬拆成策略模式,属于过度设计。
- 多个策略共享大量内部状态时,硬抽成独立类反而别扭,要考虑状态放在哪里更合理。
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("大火炒饭")
与策略模式的对比:模板方法靠继承定骨架(父类定流程、子类填步骤),策略靠组合换整块算法(把算法整体封装成对象)。模板方法在"流程不变、个别步骤多变"时最合适。
⚠️ 常见错误
- 骨架里步骤有先后依赖(必须先备料再下锅),子类重写时打乱顺序会破坏算法正确性——关键顺序应放在不可重写的骨架方法里。
- 父类的"钩子方法"(可选扩展点)过多,子类被迫实现一堆不关心的步骤。
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} 天")
用途:中间件、日志级别过滤、表单校验链。好处是请求的发送者不需要知道谁会处理,只把请求扔进链里即可。
⚠️ 常见错误
- 链太长、节点逻辑重叠,请求最终被谁处理难以定位,调试成本高。
- 请求沿链传到最后没人接,结果"静默丢失"——应设计兜底处理(如记录日志、抛异常),避免无声失败。
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 判断当前状态,状态模式把"每种状态下该做什么"分散到各自的状态类中,新增状态只需新增一个类,符合开闭原则。
⚠️ 常见错误
- 状态对象被多个对象共享,又带有可变数据时,要注意并发环境下的状态污染。
- 状态转换逻辑分散在各个状态类里,转换条件复杂时反而难以追踪整体流程——必要时用一张转换表集中说明。
📌 术语对照(中英)
| 中文 | English |
|---|---|
| 观察者 | Observer |
| 策略 | Strategy |
| 模板方法 | Template Method |
| 责任链 | Chain of Responsibility |
| 状态 | State |
🎯 复习清单
- 五种行为型模式各解决什么"协作问题",分别用一个生活例子说明。
- 策略 vs 模板方法的本质区别(组合换整块算法 vs 继承定骨架)。
- 观察者模式的一对多依赖与解耦价值,以及内存泄漏风险。
- 责任链的传递机制与兜底处理。
- 状态模式如何把分支判断拆成状态类。