行为型模式:对象之间的默契配合
行为型模式关心的核心问题:对象之间怎么通信?职责怎么分配?流程怎么编排? 让多个对象各司其职又协作顺畅,而不是一坨代码从头写到尾。
前言
创建型管"对象怎么来",结构型管"对象怎么搭",行为型管"对象怎么动"。
写业务时最头疼的就是:
- 一个操作要通知一堆人——观察者
- 流程有多个步骤,但中间步骤可能变化——模板方法
- 同一件事有多种做法,要能切换——策略
- 操作要支持撤销——命令
- 对象状态变了,行为也跟着变——状态
行为型模式一共 11 种,这里挑最常用的 8 种详讲,另外 3 种简述。
一、策略模式(Strategy)
一句话
定义一系列算法,把它们各自封装起来,让它们可以互相替换。
策略模式的核心不是"封装"本身,而是把"用哪个算法"的决定权从执行者手里拿走,交给调用方。执行者只管执行,选择谁来执行由外部决定。
生活中的例子
- 导航软件选路线:同样从 A 到 B,可以选"最短距离"、"最少时间"、"避开高速"、"避开收费"——每种是一个策略,随时切换。导航引擎本身不变,换的只是路线计算算法。
- 商场打折:满减、打折、买一送一、会员价——不同促销活动就是不同策略,换季了就切换。收银系统不需要改,只需要插入新的促销策略。
- 支付方式:支付宝、微信、银行卡、货到付款——结账时选一种,背后执行逻辑完全不同。
- 排序算法:小数组用插入排序,大数组用快排,基本有序用 TimSort——根据数据特征切换算法。
为什么需要它
电商系统算运费,一开始只有两种规则,直接 if-else 写完了:
// 第一版,看起来还好
if (type.equals("free")) {
return 0;
} else if (type.equals("weight")) {
return weight * 5;
}
半年后,业务加了 VIP 折扣、节日特惠、跨城配送、保价服务……代码变成这样:
// 不用策略模式的写法——if-else 地狱
if (type.equals("free")) {
return 0;
} else if (type.equals("weight")) {
return weight * 5;
} else if (type.equals("distance")) {
return distance * 2;
} else if (type.equals("vip")) {
return weight * 5 * 0.8;
} else if (type.equals("festival")) {
return weight * 3;
} else if (type.equals("insurance")) {
return weight * 5 + price * 0.01;
}
// 每加一种就多一个 if,整个方法越来越长
// 改 VIP 逻辑要在这里找,改节日逻辑也要在这里找
// 测试一个策略,却要加载整个方法
这段代码有三个问题:
- 新增策略要改这个方法——违反了开闭原则,应该对扩展开放、对修改关闭
- 所有策略挤在一个类里——互相干扰,一个策略改错了影响所有人
- 无法复用——另一个模块也要算运费,只能复制粘贴这一坨 if-else
策略模式的解法:把每个 if 分支拆成一个独立的类。
什么时候用,什么时候别用
适合用策略模式:
- if-else / switch 里每个分支逻辑超过几行,且预期还会继续增加
- 同一件事有多种实现方式,且需要在运行时切换(比如 A/B 测试不同推荐算法)
- 多个类只有行为不同,可以提取成策略让主类瘦下来
不适合用策略模式:
- 只有两三种固定策略,且以后也不会增加——直接写死或简单 if-else 更清晰
- 策略之间需要共享大量内部状态——说明它们本质上不是独立算法,可能需要模板方法
- 调用方需要知道策略的具体类型才能工作——这破坏了策略模式的封装前提
示意图
代码
java代码
// ── Strategy 接口 ──────────────────────────────────────────
// 所有运费策略的统一契约,Context 只依赖这个接口
// 新增策略只需实现这个接口,不需要改任何现有代码(开闭原则)
public interface ShippingStrategy {
double calculate(Order order);
String getName(); // 策略的可读名称,方便打日志和监控
}
// ── ConcreteStrategy:各策略独立,互不干扰 ─────────────────
public class FreeShipping implements ShippingStrategy {
public double calculate(Order order) {
return 0; // 免运费,直接返回 0
}
public String getName() { return "免运费"; }
}
public class WeightBasedShipping implements ShippingStrategy {
private static final double RATE = 5.0; // 每公斤 5 元,提取为常量便于调整
public double calculate(Order order) {
return order.getWeight() * RATE;
}
public String getName() { return "按重量计费"; }
}
public class DistanceBasedShipping implements ShippingStrategy {
public double calculate(Order order) {
return order.getDistance() * 2; // 每公里 2 元
}
public String getName() { return "按距离计费"; }
}
// VipShipping 用"组合"而非"继承"扩展现有策略
// 好处:不需要为每种基础策略都写一个 VIP 版本(避免类爆炸)
public class VipShipping implements ShippingStrategy {
private final ShippingStrategy baseStrategy; // 被包装的基础策略
private final double discount; // 折扣系数,如 0.8 = 8折
public VipShipping(ShippingStrategy base, double discount) {
this.baseStrategy = base;
this.discount = discount;
}
public double calculate(Order order) {
// 先用基础策略算出原价,再乘以折扣
return baseStrategy.calculate(order) * discount;
}
public String getName() { return "VIP 折扣(" + (discount * 10) + "折)"; }
}
// ── Context:上下文 ────────────────────────────────────────
// 只依赖 ShippingStrategy 接口,不关心具体是哪个实现
// 运行时可通过 setStrategy() 随时替换算法,不需要重建对象
public class ShippingCalculator {
private ShippingStrategy strategy; // 面向接口编程,持有的是接口引用
public ShippingCalculator(ShippingStrategy strategy) {
this.strategy = strategy;
}
// 运行时热切换策略——比如用户升级为 VIP 后立刻生效
public void setStrategy(ShippingStrategy strategy) {
this.strategy = strategy;
}
public double getShippingCost(Order order) {
// 把实际计算完全委托给策略对象,自己不写任何算法逻辑
double cost = strategy.calculate(order);
System.out.printf("策略[%s] 运费: %.2f 元%n", strategy.getName(), cost);
return cost;
}
}
// ── Client:使用 ───────────────────────────────────────────
// 新增策略不改 ShippingCalculator,只新增实现类,符合开闭原则
ShippingCalculator calc = new ShippingCalculator(new WeightBasedShipping());
calc.getShippingCost(order); // 策略[按重量计费] 运费: 25.00 元
// 用户升级 VIP:切换策略,Calculator 代码完全不变
calc.setStrategy(new VipShipping(new WeightBasedShipping(), 0.8));
calc.getShippingCost(order); // 策略[VIP 折扣(8.0折)] 运费: 20.00 元
上面 VipShipping 组合了基础策略,这是策略模式的一个常见用法——策略可以嵌套组合,避免写 VipWeightShipping、VipDistanceShipping 这类笛卡尔积式的爆炸子类。
python代码
from abc import ABC, abstractmethod
# ── Strategy 抽象基类 ───────────────────────────────────────
# 用 ABC 强制子类必须实现 calculate(),相当于 Java 的接口
class ShippingStrategy(ABC):
@abstractmethod
def calculate(self, order) -> float:
pass
# ── ConcreteStrategy ────────────────────────────────────────
class FreeShipping(ShippingStrategy):
def calculate(self, order) -> float:
return 0 # 免运费
class WeightBasedShipping(ShippingStrategy):
def calculate(self, order) -> float:
return order.weight * 5 # 5元/kg
class VipShipping(ShippingStrategy):
def __init__(self, base: ShippingStrategy, discount: float):
self._base = base # 组合:包装任意一种基础策略
self._discount = discount # 0.8 = 8折
def calculate(self, order) -> float:
# 委托基础策略计算,再应用折扣
return self._base.calculate(order) * self._discount
# ── Context ─────────────────────────────────────────────────
class ShippingCalculator:
def __init__(self, strategy: ShippingStrategy):
self._strategy = strategy # 依赖注入,持有接口引用
def set_strategy(self, strategy: ShippingStrategy):
self._strategy = strategy # 运行时替换,无需重建对象
def get_cost(self, order) -> float:
return self._strategy.calculate(order) # 委托,自己不算
# ── 使用 ─────────────────────────────────────────────────────
calc = ShippingCalculator(WeightBasedShipping())
print(calc.get_cost(order)) # 25.0
# 切换策略:一行代码,上下文对象不变
calc.set_strategy(VipShipping(WeightBasedShipping(), 0.8))
print(calc.get_cost(order)) # 20.0
框架中的实际应用
- Spring Security 的
AuthenticationStrategy:同一个认证流程,可以切换"第一个成功即可"、"全部必须成功"、"必须一个也不成功"等策略。 - Java
Comparator:Collections.sort(list, comparator)中的comparator就是一个策略,决定怎么排序。list.sort((a, b) -> a.name.compareTo(b.name))这个 lambda 也是策略。 - Hibernate 的缓存策略、锁策略:读写缓存、只读缓存、非严格读写——同一套 ORM 逻辑,插入不同的缓存策略。
- Webpack / Vite 的压缩策略:构建时根据环境切换
terser、esbuild、不压缩——典型的策略模式。
二、观察者模式(Observer)
一句话
定义一对多的依赖关系,一个对象状态变了,所有依赖它的对象自动收到通知。
生活中的例子
- 微信公众号:你关注了一个号,博主发文章,所有粉丝同时收到推送。你不用每天打开看有没有更新——有更新它主动告诉你。
- 股票价格提醒:设好"茅台跌破 1500 提醒我"。股价一变,系统自动通知所有设了提醒的人。
- 红绿灯:灯变绿,所有等着的车同时收到"可以走了"的信号。灯不需要一辆辆通知。
- 群聊消息:一个人在群里发消息,群里所有人都收到。发送者不需要知道群里有谁。
结构图
代码
java代码
// 观察者接口
public interface EventListener {
void onEvent(String eventType, Object data);
}
// 事件中心(被观察者 / 发布者)
public class EventBus {
private Map<String, List<EventListener>> listeners = new HashMap<>();
public void subscribe(String event, EventListener listener) {
listeners.computeIfAbsent(event, k -> new ArrayList<>()).add(listener);
}
public void unsubscribe(String event, EventListener listener) {
List<EventListener> subs = listeners.get(event);
if (subs != null) subs.remove(listener);
}
public void publish(String event, Object data) {
List<EventListener> subs = listeners.getOrDefault(event, List.of());
for (EventListener listener : subs) {
listener.onEvent(event, data);
}
}
}
// 具体观察者
public class EmailNotifier implements EventListener {
public void onEvent(String eventType, Object data) {
System.out.println("📧 发邮件通知: [" + eventType + "] " + data);
}
}
public class SmsNotifier implements EventListener {
public void onEvent(String eventType, Object data) {
System.out.println("📱 发短信通知: [" + eventType + "] " + data);
}
}
public class LogRecorder implements EventListener {
public void onEvent(String eventType, Object data) {
System.out.println("📝 记录日志: [" + eventType + "] " + data);
}
}
// 使用
EventBus bus = new EventBus();
bus.subscribe("order.created", new EmailNotifier());
bus.subscribe("order.created", new SmsNotifier());
bus.subscribe("order.created", new LogRecorder());
bus.publish("order.created", "订单#12345");
// 📧 发邮件通知: [order.created] 订单#12345
// 📱 发短信通知: [order.created] 订单#12345
// 📝 记录日志: [order.created] 订单#12345
python代码
from collections import defaultdict
class EventBus:
def __init__(self):
self._listeners = defaultdict(list)
def subscribe(self, event: str, callback):
self._listeners[event].append(callback)
def publish(self, event: str, data=None):
for callback in self._listeners[event]:
callback(event, data)
# 观察者(Python 里用函数就行,不一定要类)
def email_notifier(event, data):
print(f"📧 邮件: [{event}] {data}")
def sms_notifier(event, data):
print(f"📱 短信: [{event}] {data}")
def log_recorder(event, data):
print(f"📝 日志: [{event}] {data}")
# 使用
bus = EventBus()
bus.subscribe("user.registered", email_notifier)
bus.subscribe("user.registered", sms_notifier)
bus.subscribe("order.paid", log_recorder)
bus.publish("user.registered", "用户张三")
# 📧 邮件: [user.registered] 用户张三
# 📱 短信: [user.registered] 用户张三
Spring 的
ApplicationEvent、前端的addEventListener、Vue 的$emit、消息队列的 Pub/Sub,本质全是观察者模式。
三、模板方法模式(Template Method)
一句话
在父类中定义算法的骨架,把某些步骤延迟到子类实现。
生活中的例子
- 泡茶 vs 泡咖啡:步骤都是"烧水 → 冲泡 → 倒杯子 → 加调料",但"冲泡"和"加调料"不同:茶是泡茶叶+加柠檬,咖啡是冲咖啡粉+加糖奶。
- 考试:所有学生的考试流程一样(发卷 → 答题 → 收卷 → 评分),但每个人答的内容不同。
- 做菜:热锅 → 放油 → 放食材 → 调味 → 出锅。每道菜的"食材"和"调味"不同,但流程骨架一样。
结构图
三个角色:
- AbstractClass(抽象父类):定义流程骨架,固定步骤自己实现,变化步骤声明为抽象
- ConcreteClass(具体子类):只实现变化的步骤,流程顺序由父类控制
- 模板方法:
final修饰,防止子类修改流程顺序
代码
java代码
public abstract class BeverageMaker {
// 模板方法:定义流程骨架(final 防子类篡改流程)
public final void prepare() {
boilWater();
brew(); // 抽象:子类实现
pourInCup();
addCondiments(); // 抽象:子类实现
}
// 固定步骤
private void boilWater() { System.out.println("烧水"); }
private void pourInCup() { System.out.println("倒入杯中"); }
// 变化步骤:子类决定
protected abstract void brew();
protected abstract void addCondiments();
}
public class TeaMaker extends BeverageMaker {
protected void brew() { System.out.println("浸泡茶叶 3 分钟"); }
protected void addCondiments() { System.out.println("加柠檬片"); }
}
public class CoffeeMaker extends BeverageMaker {
protected void brew() { System.out.println("冲泡咖啡粉"); }
protected void addCondiments() { System.out.println("加糖和牛奶"); }
}
// 使用
new TeaMaker().prepare();
// 烧水 → 浸泡茶叶 3 分钟 → 倒入杯中 → 加柠檬片
new CoffeeMaker().prepare();
// 烧水 → 冲泡咖啡粉 → 倒入杯中 → 加糖和牛奶
python代码
from abc import ABC, abstractmethod
class BeverageMaker(ABC):
def prepare(self):
"""模板方法:流程骨架固定"""
self._boil_water()
self.brew() # 子类实现
self._pour_in_cup()
self.add_condiments() # 子类实现
def _boil_water(self):
print("🔥 烧水")
def _pour_in_cup(self):
print("☕ 倒入杯中")
@abstractmethod
def brew(self):
"""冲泡方式——子类决定"""
pass
@abstractmethod
def add_condiments(self):
"""加料——子类决定"""
pass
class TeaMaker(BeverageMaker):
def brew(self):
print("🍵 浸泡茶叶 3 分钟")
def add_condiments(self):
print("🍋 加柠檬片")
class CoffeeMaker(BeverageMaker):
def brew(self):
print("☕ 冲泡咖啡粉")
def add_condiments(self):
print("🥛 加糖和牛奶")
TeaMaker().prepare()
print("---")
CoffeeMaker().prepare()
流程只定义一次,变化的部分交给子类。好莱坞原则:"别打电话给我们,我们会打给你。"
四、命令模式(Command)
一句话
把一个请求封装成一个对象,从而支持撤销、排队、日志等操作。
生活中的例子
- 餐厅点菜:你对服务员说"一份红烧肉"。服务员写到单子上(命令对象),拿到厨房。厨师按单做菜。如果你说"那道菜取消",服务员撕掉那张单(撤销)。
- 遥控器按钮:遥控器上每个按钮都是一个命令——开灯、关灯、调亮度。按钮不知道灯怎么工作,它只知道"触发这个命令"。
- Git commit:每次 commit 是一个命令对象,记录了做了什么改动。
git revert就是执行这个命令的 undo。 - Excel 的 Ctrl+Z:每个操作被记成命令对象,按 Ctrl+Z 就是取出最近一个命令执行它的 undo。
结构图
四个角色:
- Command(接口):统一
execute()+undo(),Invoker 只认识它 - ConcreteCommand(具体命令):封装一次操作 + 它的撤销逻辑,持有 Receiver 引用
- Invoker(调用者):管理命令历史栈,不关心命令干了什么
- Receiver(接收者):真正执行操作的对象,被命令持有
代码
java代码
// 命令接口
public interface Command {
// execute 负责正向执行,undo 负责把这次执行撤回来
void execute();
void undo();
}
// 具体命令:插入文本
public class InsertTextCommand implements Command {
// 命令对象会把一次操作所需的上下文都封装起来
private TextEditor editor;
private String text;
private int position;
public InsertTextCommand(TextEditor editor, String text, int position) {
this.editor = editor;
this.text = text;
this.position = position;
}
@Override
public void execute() {
// 真正改文本的细节交给接收者 TextEditor
editor.insert(text, position);
}
@Override
public void undo() {
// 插入的逆操作就是删掉刚才插入的那段内容
editor.delete(position, text.length());
}
}
// 具体命令:删除文本
public class DeleteTextCommand implements Command {
private TextEditor editor;
// 删除前先记录被删内容,撤销时才能恢复
private String deletedText;
private int position;
@Override
public void execute() {
// 先取快照,再删除
deletedText = editor.getTextAt(position, length);
editor.delete(position, length);
}
@Override
public void undo() {
editor.insert(deletedText, position); // 删除的逆操作是插入
}
}
// 调用者:管理命令历史
public class CommandHistory {
// 栈结构天然适合撤销场景:最后执行的命令先撤销
private Deque<Command> history = new ArrayDeque<>();
public void execute(Command cmd) {
cmd.execute();
// 执行成功后入栈,后面 undo 才知道该撤销谁
history.push(cmd);
}
public void undo() {
if (!history.isEmpty()) {
// 取出最近一条命令,执行它自己的逆操作
Command cmd = history.pop();
cmd.undo();
}
}
}
python代码
from abc import ABC, abstractmethod
from collections import deque
class Command(ABC):
@abstractmethod
def execute(self): pass
@abstractmethod
def undo(self): pass
class InsertTextCommand(Command):
def __init__(self, editor, text, position):
self.editor = editor
self.text = text
self.position = position
def execute(self):
# 命令保存了插入内容和位置,所以调用方不需要关心细节
self.editor.content = (
self.editor.content[:self.position]
+ self.text
+ self.editor.content[self.position:]
)
def undo(self):
# 撤销插入:删掉刚刚插入的那一段
self.editor.content = (
self.editor.content[:self.position]
+ self.editor.content[self.position + len(self.text):]
)
class TextEditor:
def __init__(self):
self.content = ""
# 历史命令用双端队列保存,逻辑上当作栈使用
self._history = deque()
def execute(self, command: Command):
command.execute()
# 每执行一次命令,就把它压入历史栈
self._history.append(command)
def undo(self):
if self._history:
# 后进先出,撤销最近一次操作
cmd = self._history.pop()
cmd.undo()
# 使用
editor = TextEditor()
editor.execute(InsertTextCommand(editor, "Hello", 0))
print(editor.content) # Hello
editor.execute(InsertTextCommand(editor, " World", 5))
print(editor.content) # Hello World
editor.undo()
print(editor.content) # Hello
editor.undo()
print(editor.content) # (空)
五、状态模式(State)
一句话
允许对象在内部状态改变时改变它的行为,看起来像换了一个类。
生活中的例子
- 红绿灯:红灯状态只能停,绿灯状态只能走,黄灯状态要减速。灯的行为完全取决于当前状态,到了时间自动切换。
- 手机:锁屏状态按电源键解锁,解锁状态按电源键锁屏,关机状态按电源键开机——同一个按钮,不同状态做不同的事。
- 电梯:开门状态不能运行,运行状态不能开门,故障状态什么都不能做——每个状态有自己的规则。
- 网购订单:待支付→已支付→已发货→已收货。每个状态下能做的操作不同(待支付可以取消,已发货不能取消只能拒收)。
结构图
三个角色:
- State(状态接口):定义所有状态下可触发的行为,Context 只认识它
- ConcreteState(具体状态):实现该状态允许的操作,不允许的操作直接报错,并负责切换到下一个状态
- Context(上下文):持有当前状态,把行为委托给它,自己不写 if-else
代码
java代码
// 状态接口
public interface OrderState {
// 同一组动作在不同状态下会有不同结果,避免在一个类里堆满 if-else
void pay(OrderContext ctx);
void ship(OrderContext ctx);
void receive(OrderContext ctx);
void cancel(OrderContext ctx);
String getStatus();
}
// 待支付状态
public class PendingState implements OrderState {
public void pay(OrderContext ctx) {
System.out.println("✅ 支付成功");
// 状态迁移:待支付 -> 已支付
ctx.setState(new PaidState());
}
public void ship(OrderContext ctx) {
// 当前状态不允许的操作,直接在该状态类中拦截
System.out.println("❌ 未支付不能发货");
}
public void receive(OrderContext ctx) {
System.out.println("❌ 未支付不能确认收货");
}
public void cancel(OrderContext ctx) {
System.out.println("✅ 订单已取消");
// 状态迁移:待支付 -> 已取消
ctx.setState(new CancelledState());
}
public String getStatus() { return "待支付"; }
}
// 已支付状态
public class PaidState implements OrderState {
public void pay(OrderContext ctx) {
System.out.println("❌ 已支付,请勿重复");
}
public void ship(OrderContext ctx) {
System.out.println("✅ 已发货");
// 状态迁移:已支付 -> 已发货
ctx.setState(new ShippedState());
}
public void receive(OrderContext ctx) {
System.out.println("❌ 未发货不能收货");
}
public void cancel(OrderContext ctx) {
System.out.println("✅ 已退款并取消");
// 状态迁移:已支付 -> 已取消
ctx.setState(new CancelledState());
}
public String getStatus() { return "已支付"; }
}
// 上下文
public class OrderContext {
// 上下文只维护“当前状态是谁”,不自己判断业务分支
private OrderState state = new PendingState();
public void setState(OrderState state) { this.state = state; }
// 对外暴露稳定 API,内部行为由当前状态对象决定
public void pay() { state.pay(this); }
public void ship() { state.ship(this); }
public void receive() { state.receive(this); }
public void cancel() { state.cancel(this); }
public String getStatus() { return state.getStatus(); }
}
python代码
from abc import ABC, abstractmethod
class OrderState(ABC):
@abstractmethod
def pay(self, order): pass
@abstractmethod
def ship(self, order): pass
@abstractmethod
def cancel(self, order): pass
class PendingState(OrderState):
def pay(self, order):
print("✅ 支付成功")
order.state = PaidState()
def ship(self, order):
print("❌ 未支付不能发货")
def cancel(self, order):
print("✅ 订单已取消")
order.state = CancelledState()
class PaidState(OrderState):
def pay(self, order):
print("❌ 已支付,请勿重复")
def ship(self, order):
print("✅ 已发货")
order.state = ShippedState()
def cancel(self, order):
print("✅ 已退款并取消")
order.state = CancelledState()
class ShippedState(OrderState):
def pay(self, order):
print("❌ 已发货")
def ship(self, order):
print("❌ 已发货")
def cancel(self, order):
print("❌ 已发货不能取消,请拒收")
class CancelledState(OrderState):
def pay(self, order):
print("❌ 订单已取消")
def ship(self, order):
print("❌ 订单已取消")
def cancel(self, order):
print("❌ 订单已取消")
class Order:
def __init__(self):
self.state = PendingState()
def pay(self):
self.state.pay(self)
def ship(self):
self.state.ship(self)
def cancel(self):
self.state.cancel(self)
# 使用
order = Order()
order.ship() # ❌ 未支付不能发货
order.pay() # ✅ 支付成功
order.pay() # ❌ 已支付,请勿重复
order.ship() # ✅ 已发货
order.cancel() # ❌ 已发货不能取消,请拒收
六、责任链模式(Chain of Responsibility)
一句话
把请求沿着一条链传递,每个节点自己决定处理还是传给下一个。
生活中的例子
- 公司请假审批:请 1 天组长批,请 3 天经理批,请 7 天总监批,超过 7 天找 CEO。你只需要提交申请,系统自动沿链条往上传。
- 客服系统:用户问题先到机器人,机器人答不了转人工,人工客服搞不定转主管,主管搞不定转经理。
- Java 异常处理:异常从内层 catch 往外层抛,直到有人处理。
- 中间件管道:一个 HTTP 请求经过鉴权→限流→日志→业务处理,每一层都可以决定"放行"还是"拦截"。
结构图
代码实现
java代码
public abstract class Approver {
protected Approver next;
protected String name;
public Approver(String name) { this.name = name; }
public Approver setNext(Approver next) {
this.next = next;
return next; // 支持链式调用
}
public abstract void approve(int days, String reason);
}
public class TeamLead extends Approver {
public TeamLead() { super("组长"); }
public void approve(int days, String reason) {
if (days <= 1) {
System.out.println(name + "批准: " + days + "天, 理由: " + reason);
} else if (next != null) {
next.approve(days, reason);
}
}
}
public class Manager extends Approver {
public Manager() { super("经理"); }
public void approve(int days, String reason) {
if (days <= 3) {
System.out.println(name + "批准: " + days + "天, 理由: " + reason);
} else if (next != null) {
next.approve(days, reason);
}
}
}
public class Director extends Approver {
public Director() { super("总监"); }
public void approve(int days, String reason) {
if (days <= 7) {
System.out.println(name + "批准: " + days + "天, 理由: " + reason);
} else {
System.out.println("需要 CEO 审批: " + days + "天");
}
}
}
// 组装链条
Approver chain = new TeamLead();
chain.setNext(new Manager()).setNext(new Director());
chain.approve(1, "看病"); // 组长批准: 1天
chain.approve(3, "出差"); // 经理批准: 3天
chain.approve(5, "旅游"); // 总监批准: 5天
chain.approve(15, "长假"); // 需要 CEO 审批: 15天
python代码
from abc import ABC, abstractmethod
class Approver(ABC):
def __init__(self, name):
self.name = name
self._next = None
def set_next(self, approver):
self._next = approver
return approver # 链式调用
@abstractmethod
def approve(self, days: int, reason: str):
pass
class TeamLead(Approver):
def approve(self, days, reason):
if days <= 1:
print(f"✅ {self.name}批准: {days}天 ({reason})")
elif self._next:
self._next.approve(days, reason)
class Manager(Approver):
def approve(self, days, reason):
if days <= 3:
print(f"✅ {self.name}批准: {days}天 ({reason})")
elif self._next:
self._next.approve(days, reason)
class Director(Approver):
def approve(self, days, reason):
if days <= 7:
print(f"✅ {self.name}批准: {days}天 ({reason})")
else:
print(f"⚠️ 需CEO审批: {days}天 ({reason})")
# 组装链条
chain = TeamLead("组长")
chain.set_next(Manager("经理")).set_next(Director("总监"))
chain.approve(1, "看牙") # ✅ 组长批准: 1天 (看牙)
chain.approve(3, "出差") # ✅ 经理批准: 3天 (出差)
chain.approve(5, "婚假") # ✅ 总监批准: 5天 (婚假)
chain.approve(20, "环游世界") # ⚠️ 需CEO审批: 20天 (环游世界)
七、迭代器模式(Iterator)
一句话
提供一种方法顺序访问集合中的元素,而不暴露集合的内部表示。
生活中的例子
- 翻书:你不需要知道书是怎么装订的,只需要"翻下一页"就能看到所有内容。
- 电视遥控器翻台:按"下一个频道"就行,不用管电视信号怎么存储的。
- 音乐播放列表:点"下一首",不用管歌曲是存在数组里还是链表里。
结构图
两个角色:
- Iterator(迭代器接口):统一
hasNext()+next(),调用方只认识它 - IterableCollection(集合接口):提供
createIterator()工厂方法,隐藏内部存储结构
代码
java代码
public interface Iterator<T> {
// 是否还有下一个元素可读
boolean hasNext();
// 取出当前元素,并把游标向后移动
T next();
}
public interface IterableCollection<T> {
// 集合负责创建迭代器,而不是把底层存储结构直接暴露出去
Iterator<T> createIterator();
}
// 自定义集合:底层用数组
public class BookShelf implements IterableCollection<String> {
private String[] books;
private int count = 0;
public BookShelf(int capacity) { books = new String[capacity]; }
public void addBook(String book) { books[count++] = book; } // 依次写入数组尾部
@Override
public Iterator<String> createIterator() {
// 每次遍历都返回一个新的迭代器,避免多个遍历过程互相影响
return new BookIterator();
}
// 内部迭代器
private class BookIterator implements Iterator<String> {
// 游标属于迭代器自己,不污染 BookShelf 的状态
private int index = 0;
public boolean hasNext() { return index < count; } // 只遍历有效元素
public String next() { return books[index++]; } // 先返回,再后移
}
}
// 使用——不需要知道底层是数组还是链表
BookShelf shelf = new BookShelf(10);
shelf.addBook("设计模式");
shelf.addBook("重构");
shelf.addBook("代码整洁之道");
Iterator<String> it = shelf.createIterator();
while (it.hasNext()) {
// 客户端只依赖统一迭代接口,不关心底层到底是数组还是链表
System.out.println(it.next());
}
Python 天生支持迭代器协议(__iter__ + __next__):
python代码
class BookShelf:
def __init__(self):
self._books = []
def add_book(self, book):
self._books.append(book)
def __iter__(self):
"""实现迭代器协议"""
# 开始遍历时重置游标,并把自己作为迭代器返回
self._index = 0
return self
def __next__(self):
if self._index >= len(self._books):
# 告诉 for 循环:元素取完了,可以结束遍历
raise StopIteration
book = self._books[self._index]
self._index += 1
return book
# 使用——直接 for 循环
shelf = BookShelf()
shelf.add_book("设计模式")
shelf.add_book("重构")
shelf.add_book("代码整洁之道")
for book in shelf: # Python 自动调用 __iter__ 和 __next__
print(book)
八、中介者模式(Mediator)
一句话
用一个中介对象封装一系列对象之间的交互,让对象之间不直接引用,降低耦合。
生活中的例子
- 机场塔台:天上 50 架飞机,如果互相喊话决定谁先降落,就乱了。全部跟塔台通信,由塔台统一调度。
- 微信群聊:群里 100 人,不是每个人跟其他 99 人建立连接。所有人发消息到"群"(中介),群帮你转发给其他人。
- 房产中介:买家和卖家不直接认识,通过中介撮合交易。
- MVC 中的 Controller:View 和 Model 不直接对话,通过 Controller 协调。
结构图
两个角色:
- Mediator(中介者):接管所有成员之间的通信,集中处理路由逻辑
- Colleague(同事类):只持有中介者引用,通过中介者发消息,不直接引用其他同事
代码
java代码
// 中介者
public class ChatRoom {
// 中介者统一维护成员引用,成员之间不直接相互持有
private Map<String, User> users = new HashMap<>();
public void register(User user) {
users.put(user.getName(), user);
// 注册时把聊天室回填给用户,后续用户只和聊天室通信
user.setChatRoom(this);
}
public void sendMessage(String msg, User sender, String receiver) {
// 私聊:由中介者决定把消息路由给谁
User target = users.get(receiver);
if (target != null) {
target.receive(msg, sender.getName());
}
}
public void broadcast(String msg, User sender) {
// 群发:除了发送者自己,其他所有成员都收到消息
for (User user : users.values()) {
if (user != sender) {
user.receive(msg, sender.getName());
}
}
}
}
public class User {
private String name;
// 用户只认识聊天室,不直接依赖其他用户
private ChatRoom room;
public User(String name) { this.name = name; }
public String getName() { return name; }
public void setChatRoom(ChatRoom room) { this.room = room; }
public void send(String msg) {
System.out.println(name + " 发送: " + msg);
// 发送消息时不自己找接收方,而是交给中介者协调
room.broadcast(msg, this);
}
public void sendTo(String msg, String receiver) {
// 私聊同理,仍然通过中介者中转
room.sendMessage(msg, this, receiver);
}
public void receive(String msg, String from) {
System.out.println(name + " 收到来自 " + from + ": " + msg);
}
}
python代码
class ChatRoom:
"""中介者:管理所有用户间的通信"""
def __init__(self):
self._users: dict[str, 'User'] = {}
def register(self, user):
# 统一登记用户,并把聊天室注入进去
self._users[user.name] = user
user.room = self
def send(self, msg, sender, receiver=None):
if receiver:
# 私聊
if receiver in self._users:
self._users[receiver].receive(msg, sender.name)
else:
# 广播
for name, user in self._users.items():
if user != sender:
user.receive(msg, sender.name)
class User:
def __init__(self, name):
self.name = name
self.room = None
def send(self, msg, to=None):
print(f"💬 {self.name} 发送: {msg}")
# 用户只表达“发什么、发给谁”,实际路由交给聊天室
self.room.send(msg, self, to)
def receive(self, msg, from_name):
print(f" 📩 {self.name} 收到来自 {from_name}: {msg}")
# 使用
room = ChatRoom()
alice = User("Alice")
bob = User("Bob")
charlie = User("Charlie")
room.register(alice)
room.register(bob)
room.register(charlie)
alice.send("大家好!")
# 💬 Alice 发送: 大家好!
# 📩 Bob 收到来自 Alice: 大家好!
# 📩 Charlie 收到来自 Alice: 大家好!
bob.send("明天开会", to="Alice") # 私聊
# 💬 Bob 发送: 明天开会
# 📩 Alice 收到来自 Bob: 明天开会
N 个用户之间的 N×N 关系,变成了 N 个用户和 1 个中介的 N 条关系。
剩余三种(简述)
| 模式 | 核心 | 生活比喻 | 代码中的应用 |
|---|---|---|---|
| 备忘录(Memento) | 保存和恢复对象状态 | 游戏存档——随时 Save/Load | 编辑器撤销、数据库事务回滚 |
| 访问者(Visitor) | 不修改类就能加新操作 | 税务审计员——不改账本结构,但能跑不同审计规则 | 编译器 AST 遍历、报表生成 |
| 解释器(Interpreter) | 给语言定义语法并解释 | 计算器——把 "3+5*2" 变成可执行的计算 | SQL 解析、正则表达式引擎、规则引擎 |
总结:行为型模式选型指南
| 我的问题 | 用什么模式 | 一句话判断 |
|---|---|---|
| 同一件事有多种做法,要能切换 | 策略模式 | "用哪个算法由外部决定" |
| 一个变化要通知多个对象 | 观察者模式 | "一对多的广播" |
| 流程固定但某些步骤可变 | 模板方法 | "骨架不变,细节可变" |
| 操作要能撤销/排队/日志 | 命令模式 | "把操作变成对象" |
| 对象行为随状态变化 | 状态模式 | "当前什么状态就做什么事" |
| 请求在多个处理器间传递 | 责任链 | "一个处理不了传给下一个" |
| 遍历集合但不暴露实现 | 迭代器 | "给我下一个就行" |
| 多个对象交互太复杂 | 中介者 | "都别互相找,找中间人" |
心法
- 策略 vs 状态: 策略是"我来选用哪个"(外部驱动),状态是"对象自己根据状态切换"(内部驱动)。打比方:导航选路线是策略(你选的),红绿灯切换是状态(自动的)。
- 观察者的陷阱: 链条太长会造成事件风暴、循环触发。实际工程中建议限制层级或用异步事件总线。A 触发 B,B 触发 C,C 又触发 A——死循环。
- 模板方法 vs 策略: 模板方法用继承固定骨架,策略用组合随时替换。如果需要运行时切换,选策略;如果流程骨架永远不变,选模板方法。
- 责任链要有兜底: 链条末尾必须有一个"默认处理器",否则请求掉到空里无人处理——就像客服电话转了八个人最后掉线。
写在最后
(终于复习完了)
模式的最终目标只有一个:让代码容易改。
如果你用了模式之后代码反而更难读了,那大概率是用错了——就像两个人吃饭非要订 20 人的宴会厅,排场有了,但没必要。

行为型模式关心的核心问题:对象之间怎么通信?职责怎么分配?流程怎么编排? 让多个对象各司其职又协作顺畅,而不是一坨代码从头写到尾。
浙公网安备 33010602011771号