惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

量子位
Stack Overflow Blog
Stack Overflow Blog
人人都是产品经理
人人都是产品经理
The GitHub Blog
The GitHub Blog
Engineering at Meta
Engineering at Meta
Vercel News
Vercel News
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Y
Y Combinator Blog
The Cloudflare Blog
Last Week in AI
Last Week in AI
B
Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
T
Tailwind CSS Blog
V
Visual Studio Blog
博客园 - 三生石上(FineUI控件)
小众软件
小众软件
Google DeepMind News
Google DeepMind News
D
DataBreaches.Net
博客园 - 司徒正美
B
Blog RSS Feed
Microsoft Azure Blog
Microsoft Azure Blog
罗磊的独立博客
Hugging Face - Blog
Hugging Face - Blog
L
LangChain Blog

博客园_首页

Plist 二进制格式 Milvus 和 PGVector,哪个更好? OpenClaw 已过时?在 VS Code 中运行 Hermes Agent! 第30篇文章:一个大三计科生的自白 Manim如何在数学公式中完美显示中文? Docker 部署 RocketMQ 5 并发编程核心概念辨析 C#事务处理最佳实践:别再让“主表存了、明细丢了”的破事发生 CLI 是什么?为什么大厂突然集体卷命令行? 【从0到1构建一个ClaudeAgent】协作-自主Agent UIImageView 设置图片不生效的原因排查 最小二乘问题详解20:无先验约束下的增量式SFM自由网平差 痞子衡嵌入式:大话双核i.MXRT1180之XIP应用里借助MU实现可靠Flash IAP的方法 AI Chat 封装, SemanticKerne.AiProvider.Unified 已发布 Windows下右键编辑js文件无法打开记事本——在注册表中使用环境变量 在后台服务中使用 Scoped 服务,为什么总是报错? H200 安装驱动并使用sglang启动模型 wireshark 抓包Trap上报告警内容 我用 AI 辅助开发了一系列小工具(2):图片压缩工具 [A Primer On MC and CC] 2.1 Memory Consistency 1 - 指令重排序和 SC 模型 Oracle数据库SCN推进技术详解与实践指南 玩转控件:封装个带图片的Label控件 Claude Code 4.7 真正该升级的不是模型,而是你的工作流 前端小白一句话,AI 帮我做了个颜值拉满的桌面媒体播放器。当代码不再是门槛,一句话编程就是现实。 5. WorkBuddy: 小龙虾的灵魂三件套,让你的小龙虾不只是工具 SQLite 分片方案实战:三种分片策略的深度对比 告别简陋 UI!一款基于 Fluent Design 和基于 WinUI 的开源免费、现代化的 Avalonia UI 控件库 关于二进制排列组合枚举的总结 AI开发-python-LangGraph框架(3-27-LangGraph从零实现大模型智能决策工作流) ElasticSearch主分片和副本分片概念详解
行为型模式:对象之间的默契配合
江鸟Dev · 2026-06-22 · via 博客园_首页

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


前言

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

写业务时最头疼的就是:

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

行为型模式一共 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)

一句话

用一个中介对象封装一系列对象之间的交互,让对象之间不直接引用,降低耦合。

生活中的例子

  • 机场塔台:天上 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 人的宴会厅,排场有了,但没必要。