6. 行为型模式:策略与观察者
这是什么:策略 = 把"做某件事的算法"封装成可整体替换的一份;观察者 = 一处状态变化,自动通知所有"关注它"的对象。两者都是行为型:管对象之间怎么协作。
为什么用:结账逻辑写死 if vip: ... elif svip: ...,每上新卡种就翻老代码;商品降价要同时发短信、写日志、推送 App,把三个动作硬编在改价函数里,以后每加一个动作都得动它——策略让算法可换,观察者让通知可挂。
如何用:先用"策略字典"管理一套计价方案,再给商品装上降价观察者。
策略:按名字取算法,随换随用
把每个算法封装成函数收进字典,调用方只报名字,不碰实现:
def normal(price):
return price
def vip(price):
return round(price * 0.8, 2)
def black_friday(price):
return round(price * 0.5, 2)
STRATEGIES = {"normal": normal, "vip": vip, "bf": black_friday}
def checkout(price, kind):
return STRATEGIES[kind](price) # 按名字取函数,立刻调用
for kind in ("normal", "vip", "bf"):
print(kind, checkout(100, kind))
你会看到:
normal 100
vip 80.0
bf 50.0
checkout 一行不用改,策略随便加:定义新函数、字典里注册一个名字,完事。排序时传的 key=len、Flask 的视图函数,本质都是这个模式——函数即策略。
观察者:降价了,谁关注谁收到
商品维护一份观察者名单,价格一变就挨个通知;观察者各干各的活,商品完全不需要认识它们是谁:
class Product:
def __init__(self, name, price):
self.name = name
self.price = price
self.watchers = [] # 所有盯着这个商品的观察者
def attach(self, watcher):
self.watchers.append(watcher) # 订阅:上名单
def change_price(self, new_price):
old, self.price = self.price, new_price
if new_price < old: # 降价才通知
for w in self.watchers:
w.notify(self.name, old, new_price)
class SmsWatcher:
def notify(self, name, old, new):
print(f"[短信] {name} 降价啦:{old} → {new}")
class LogWatcher:
def notify(self, name, old, new):
print(f"[日志] 记录:{name} {old}→{new}")
kb = Product("机械键盘", 399)
kb.attach(SmsWatcher())
kb.attach(LogWatcher())
kb.change_price(299)
kb.change_price(310) # 涨价:没人被打扰
你会看到:
[短信] 机械键盘 降价啦:399 → 299
[日志] 记录:机械键盘 399→299
降价触发了两条通知,涨价一条都没有。想加"App 推送"?再写一个观察者 attach 上去,Product 一行不改——这正是观察者的价值:变化的双方互不牵连。
消息队列、前端的
addEventListener、框架的响应式更新,底层都是发布-订阅思想。
策略 vs 观察者:换算法与传消息
| 策略 | 观察者 | |
|---|---|---|
| 解决什么 | 算法要整体替换 | 一处变化要广播多处 |
| 关键动作 | 按名字取出算法执行 | 维护名单、逐个通知 |
| 方向 | 一对一:调用方 → 算法 | 一对多:主体 → 观察者们 |
本章解读
| 代码 | 作用 |
|---|---|
STRATEGIES[kind](price) |
字典存函数、按名取出即调——策略的 Python 精髓 |
self.watchers = [] |
观察者名单:主体只认接口不认人 |
attach(watcher) |
订阅入口,对应"取消订阅"一般叫 detach |
old, self.price = self.price, new_price |
元组同时赋值:先留旧值再更新,通知要用 |
记忆口诀
算法要换别写死,函数进字典按名取;变化要播别硬编,观察者上名单等通知。
动手改
- 新增策略
svip(七折),注册进STRATEGIES,算 100 元结多少 - 写一个
AppPushWatcher,通知格式为[推送] {name}:{new} 元(原价 {old}),挂上名单重跑 - 给
Product加detach(watcher)方法,把LogWatcher摘掉后再降价,验证只剩短信
下一章:综合练习:可插拔的通知中心 🐸