跳转到内容

第四部分 · 行为型模式

一句话:行为型模式解决“对象之间怎么分工、怎么沟通”的问题:算法要换、事件要通知、状态要切换时,把变化的那一块单独拿出来,而不是让它散在一堆 if 里。

对象创建好、组合好之后,剩下的麻烦出在“谁来做什么、做的时候谁知道谁”。常见的有三种。

第一种,一个函数里用 if type == … 选算法。比如运费计算:type == "standard" 走一套,"express" 走一套,"cold_chain" 再走一套。每加一种运费,就要回到这个函数里加一个分支。有人加冷链分支时顺手改了公共的重量取整,标准运费的订单就全部多收了几块钱。

第二种,一件事发生后要通知的模块越来越多。订单支付成功后先是发短信,后来又要加积分、库存、看板、风控,每加一个都要改支付代码。哪天积分服务抛了异常,后面的库存和看板都没被调用,订单却已经收了钱。

第三种,对象的行为随状态变化。一个订单有“待付款、已付款、已发货、已取消”,pay()、cancel()、ship() 每个方法里都有一串 if status == …。新增一个“退款中”状态,要把每个方法都翻一遍;漏掉 ship(),退款中的订单就被发出去了。

三种麻烦的共同点是:变化的部分(算法、通知对象、状态行为)和不变的部分缠在同一段代码里。行为型模式(Behavioral Patterns)把变化的部分拆出去,新增一种情况只需加东西,不用翻旧代码。

模式 一句话 流行度 先读哪个
策略(Strategy) 把一族可互换的算法各自封装 ★★★ 先读
观察者(Observer) 一个对象变化时自动通知所有关注者 ★★★ 先读
命令(Command) 把一个请求封装成对象 ★★★ 先读
模板方法(Template Method) 父类定流程骨架,子类填具体步骤 ★★★
状态(State) 对象的行为随内部状态切换 ★★★
迭代器(Iterator) 不暴露内部结构地逐个访问元素 ★★★
责任链(Chain of Responsibility) 请求沿一条处理者链传递 ★★
中介者(Mediator) 用一个中间对象协调多个对象的交互 ★★
备忘录(Memento) 保存并恢复对象的内部状态 ★
访问者(Visitor) 不改元素类就给它们加新操作 ★
解释器(Interpreter) 为一种简单语言定义文法并解释执行 ★

流行度的含义和第 1 章一样:★★★ 是几乎每个项目都会碰到的,★ 是知道它解决什么问题、遇到时能认出来即可。

行为型模式:问题与模式对照

左边是你会遇到的问题,右边是该看的模式和章号,一条线对应一个。

  • 你的函数里有一串 if type == … 选算法,每加一种就要改它 → 看第 17 章 策略。
  • 一件事发生后,越来越多的模块要被通知,你不想每加一个就去改发事件的代码 → 看第 18 章 观察者。
  • 你想把“一个操作”存起来,用来撤销、排队、延迟执行或记日志 → 看第 19 章 命令。
  • 好几段流程步骤都一样,只有中间两三步不同,现在是复制粘贴 → 看第 20 章 模板方法。
  • 对象的 if status 散在每个方法里,新增一个状态要全部翻一遍 → 看第 21 章 状态。
  • 你要遍历一个集合,又不想让调用方知道它里面是数组还是树 → 看第 22 章 迭代器。

建议先读第 17 章策略、第 18 章观察者、第 19 章命令。它们覆盖了日常开发里最常见的三件事:换算法、发通知、把操作当数据传。三章彼此独立,先读哪个都行。

第 21 章状态建议在策略之后读。两者的结构几乎一样,都是“一个对象持有另一个可替换的对象,把工作交给它”,区别是谁来换:策略由调用方挑,状态由对象自己随状态切换。第 20 章模板方法适合和策略对照读:同样是“一部分步骤会变”,模板方法靠继承,策略靠组合,对照着看能明白各自适合什么场合。

第 22 章迭代器在你遇到“遍历”时读,很多语言已经内置,读它主要是知道背后发生了什么。第 23 章责任链和第 24 章中介者按需读:遇到审批流、中间件,翻第 23 章;遇到一堆控件互相联动、改一个牵动一片,翻第 24 章。

第 25 章备忘录、第 26 章访问者、第 27 章解释器可以跳过。它们分别用于保存还原状态、给对象树加新操作、解释一小门自定义语言,日常业务代码里很少碰到。时间有限的话,读一下这三章开头的两行题记,知道它们解决什么问题就够了。