行为型模式:对象之间的默契配合

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


前言

创建型管"对象怎么来",结构型管"对象怎么搭",行为型管"对象怎么动"。

写业务时最头疼的就是:

  • 一个操作要通知一堆人——观察者
  • 流程有多个步骤,但中间步骤可能变化——模板方法
  • 同一件事有多种做法,要能切换——策略
  • 操作要支持撤销——命令
  • 对象状态变了,行为也跟着变——状态

行为型模式一共 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 逻辑要在这里找,改节日逻辑也要在这里找
    // 测试一个策略,却要加载整个方法

这段代码有三个问题:

  1. 新增策略要改这个方法——违反了开闭原则,应该对扩展开放、对修改关闭
  2. 所有策略挤在一个类里——互相干扰,一个策略改错了影响所有人
  3. 无法复用——另一个模块也要算运费,只能复制粘贴这一坨 if-else

策略模式的解法:把每个 if 分支拆成一个独立的类

什么时候用,什么时候别用

适合用策略模式:

  • if-else / switch 里每个分支逻辑超过几行,且预期还会继续增加
  • 同一件事有多种实现方式,且需要在运行时切换(比如 A/B 测试不同推荐算法)
  • 多个类只有行为不同,可以提取成策略让主类瘦下来

不适合用策略模式:

  • 只有两三种固定策略,且以后也不会增加——直接写死或简单 if-else 更清晰
  • 策略之间需要共享大量内部状态——说明它们本质上不是独立算法,可能需要模板方法
  • 调用方需要知道策略的具体类型才能工作——这破坏了策略模式的封装前提

示意图

c__办公区_学习项目_博客类_设计模式_diagrams_行为模式_策略模式

代码

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 组合了基础策略,这是策略模式的一个常见用法——策略可以嵌套组合,避免写 VipWeightShippingVipDistanceShipping 这类笛卡尔积式的爆炸子类。

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 SecurityAuthenticationStrategy:同一个认证流程,可以切换"第一个成功即可"、"全部必须成功"、"必须一个也不成功"等策略。
  • Java ComparatorCollections.sort(list, comparator) 中的 comparator 就是一个策略,决定怎么排序。list.sort((a, b) -> a.name.compareTo(b.name)) 这个 lambda 也是策略。
  • Hibernate 的缓存策略、锁策略:读写缓存、只读缓存、非严格读写——同一套 ORM 逻辑,插入不同的缓存策略。
  • Webpack / Vite 的压缩策略:构建时根据环境切换 terseresbuild、不压缩——典型的策略模式。

二、观察者模式(Observer)

一句话

定义一对多的依赖关系,一个对象状态变了,所有依赖它的对象自动收到通知。

生活中的例子

  • 微信公众号:你关注了一个号,博主发文章,所有粉丝同时收到推送。你不用每天打开看有没有更新——有更新它主动告诉你。
  • 股票价格提醒:设好"茅台跌破 1500 提醒我"。股价一变,系统自动通知所有设了提醒的人。
  • 红绿灯:灯变绿,所有等着的车同时收到"可以走了"的信号。灯不需要一辆辆通知。
  • 群聊消息:一个人在群里发消息,群里所有人都收到。发送者不需要知道群里有谁。

结构图

c__办公区_学习项目_博客类_设计模式_diagrams_行为模式_观察者模式

代码

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 泡咖啡:步骤都是"烧水 → 冲泡 → 倒杯子 → 加调料",但"冲泡"和"加调料"不同:茶是泡茶叶+加柠檬,咖啡是冲咖啡粉+加糖奶。
  • 考试:所有学生的考试流程一样(发卷 → 答题 → 收卷 → 评分),但每个人答的内容不同。
  • 做菜:热锅 → 放油 → 放食材 → 调味 → 出锅。每道菜的"食材"和"调味"不同,但流程骨架一样。

结构图

c__办公区_学习项目_博客类_设计模式_diagrams_行为模式_模板方法模式
三个角色:

  • 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。

结构图

c__办公区_学习项目_博客类_设计模式_diagrams_行为模式_命令模式
四个角色:

  • 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)

一句话

允许对象在内部状态改变时改变它的行为,看起来像换了一个类。

生活中的例子

  • 红绿灯:红灯状态只能停,绿灯状态只能走,黄灯状态要减速。灯的行为完全取决于当前状态,到了时间自动切换。
  • 手机:锁屏状态按电源键解锁,解锁状态按电源键锁屏,关机状态按电源键开机——同一个按钮,不同状态做不同的事。
  • 电梯:开门状态不能运行,运行状态不能开门,故障状态什么都不能做——每个状态有自己的规则。
  • 网购订单:待支付→已支付→已发货→已收货。每个状态下能做的操作不同(待支付可以取消,已发货不能取消只能拒收)。

结构图

c__办公区_学习项目_博客类_设计模式_diagrams_行为模式_状态模式

三个角色:

  • 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 请求经过鉴权→限流→日志→业务处理,每一层都可以决定"放行"还是"拦截"。

结构图

c__办公区_学习项目_博客类_设计模式_diagrams_行为模式_责任链模式

代码实现

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)

一句话

提供一种方法顺序访问集合中的元素,而不暴露集合的内部表示。

生活中的例子

  • 翻书:你不需要知道书是怎么装订的,只需要"翻下一页"就能看到所有内容。
  • 电视遥控器翻台:按"下一个频道"就行,不用管电视信号怎么存储的。
  • 音乐播放列表:点"下一首",不用管歌曲是存在数组里还是链表里。

结构图

c__办公区_学习项目_博客类_设计模式_diagrams_行为模式_迭代器模式

两个角色:

  • 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 协调。

结构图

c__办公区_学习项目_博客类_设计模式_diagrams_行为模式_中介者模式


两个角色:

  • 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 解析、正则表达式引擎、规则引擎

总结:行为型模式选型指南

我的问题 用什么模式 一句话判断
同一件事有多种做法,要能切换 策略模式 "用哪个算法由外部决定"
一个变化要通知多个对象 观察者模式 "一对多的广播"
流程固定但某些步骤可变 模板方法 "骨架不变,细节可变"
操作要能撤销/排队/日志 命令模式 "把操作变成对象"
对象行为随状态变化 状态模式 "当前什么状态就做什么事"
请求在多个处理器间传递 责任链 "一个处理不了传给下一个"
遍历集合但不暴露实现 迭代器 "给我下一个就行"
多个对象交互太复杂 中介者 "都别互相找,找中间人"

心法

  1. 策略 vs 状态: 策略是"我来选用哪个"(外部驱动),状态是"对象自己根据状态切换"(内部驱动)。打比方:导航选路线是策略(你选的),红绿灯切换是状态(自动的)。
  2. 观察者的陷阱: 链条太长会造成事件风暴、循环触发。实际工程中建议限制层级或用异步事件总线。A 触发 B,B 触发 C,C 又触发 A——死循环。
  3. 模板方法 vs 策略: 模板方法用继承固定骨架,策略用组合随时替换。如果需要运行时切换,选策略;如果流程骨架永远不变,选模板方法。
  4. 责任链要有兜底: 链条末尾必须有一个"默认处理器",否则请求掉到空里无人处理——就像客服电话转了八个人最后掉线。

写在最后

(终于复习完了)
模式的最终目标只有一个:让代码容易改

如果你用了模式之后代码反而更难读了,那大概率是用错了——就像两个人吃饭非要订 20 人的宴会厅,排场有了,但没必要。

posted @ 2026-06-22 16:38  江鸟Dev  阅读(61)  评论(0)    收藏  举报