🐸 青蛙编程网
首页 / Python / 设计模式 / 6. 行为型模式:策略与观察者

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 元组同时赋值:先留旧值再更新,通知要用

记忆口诀

算法要换别写死,函数进字典按名取;变化要播别硬编,观察者上名单等通知。

动手改

  1. 新增策略 svip(七折),注册进 STRATEGIES,算 100 元结多少
  2. 写一个 AppPushWatcher,通知格式为 [推送] {name}:{new} 元(原价 {old}),挂上名单重跑
  3. 给 Product 加 detach(watcher) 方法,把 LogWatcher 摘掉后再降价,验证只剩短信

下一章:综合练习:可插拔的通知中心 🐸

📥 下载本章代码

ZIP 内含本章全部代码文件与说明,解压即用,与教程内容同步生成。