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

发布时间:2026/6/30 4:34:42
行为型模式:对象之间的默契配合 建型管对象怎么来结构型管对象怎么搭行为型管对象怎么动。写业务时最头疼的就是一个操作要通知一堆人——观察者流程有多个步骤但中间步骤可能变化——模板方法同一件事有多种做法要能切换——策略操作要支持撤销——命令对象状态变了行为也跟着变——状态行为型模式一共 11 种这里挑最常用的 8 种详讲另外 3 种简述。一、策略模式Strategy一句话定义一系列算法把它们各自封装起来让它们可以互相替换。策略模式的核心不是封装本身而是把用哪个算法的决定权从执行者手里拿走交给调用方。执行者只管执行选择谁来执行由外部决定。生活中的例子导航软件选路线同样从 A 到 B可以选最短距离、最少时间、避开高速、避开收费——每种是一个策略随时切换。导航引擎本身不变换的只是路线计算算法。商场打折满减、打折、买一送一、会员价——不同促销活动就是不同策略换季了就切换。收银系统不需要改只需要插入新的促销策略。支付方式支付宝、微信、银行卡、货到付款——结账时选一种背后执行逻辑完全不同。排序算法小数组用插入排序大数组用快排基本有序用 TimSort——根据数据特征切换算法。为什么需要它电商系统算运费一开始只有两种规则直接 if-else 写完了java// 第一版看起来还好 if (type.equals(free)) { return 0; } else if (type.equals(weight)) { return weight * 5; }半年后业务加了 VIP 折扣、节日特惠、跨城配送、保价服务……代码变成这样java// 不用策略模式的写法——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代码上面VipShipping组合了基础策略这是策略模式的一个常见用法——策略可以嵌套组合避免写VipWeightShipping、VipDistanceShipping这类笛卡尔积式的爆炸子类。python代码框架中的实际应用Spring Security的AuthenticationStrategy同一个认证流程可以切换第一个成功即可、全部必须成功、必须一个也不成功等策略。JavaComparatorCollections.sort(list, comparator)中的comparator就是一个策略决定怎么排序。list.sort((a, b) - a.name.compareTo(b.name))这个 lambda 也是策略。Hibernate的缓存策略、锁策略读写缓存、只读缓存、非严格读写——同一套 ORM 逻辑插入不同的缓存策略。Webpack / Vite的压缩策略构建时根据环境切换terser、esbuild、不压缩——典型的策略模式。二、观察者模式Observer一句话定义一对多的依赖关系一个对象状态变了所有依赖它的对象自动收到通知。生活中的例子微信公众号你关注了一个号博主发文章所有粉丝同时收到推送。你不用每天打开看有没有更新——有更新它主动告诉你。股票价格提醒设好茅台跌破 1500 提醒我。股价一变系统自动通知所有设了提醒的人。红绿灯灯变绿所有等着的车同时收到可以走了的信号。灯不需要一辆辆通知。群聊消息一个人在群里发消息群里所有人都收到。发送者不需要知道群里有谁。结构图代码java代码python代码Spring 的ApplicationEvent、前端的addEventListener、Vue 的$emit、消息队列的 Pub/Sub本质全是观察者模式。三、模板方法模式Template Method一句话在父类中定义算法的骨架把某些步骤延迟到子类实现。生活中的例子泡茶 vs 泡咖啡步骤都是烧水 → 冲泡 → 倒杯子 → 加调料但冲泡和加调料不同茶是泡茶叶加柠檬咖啡是冲咖啡粉加糖奶。考试所有学生的考试流程一样发卷 → 答题 → 收卷 → 评分但每个人答的内容不同。做菜热锅 → 放油 → 放食材 → 调味 → 出锅。每道菜的食材和调味不同但流程骨架一样。结构图三个角色AbstractClass抽象父类定义流程骨架固定步骤自己实现变化步骤声明为抽象ConcreteClass具体子类只实现变化的步骤流程顺序由父类控制模板方法final修饰防止子类修改流程顺序代码java代码python代码流程只定义一次变化的部分交给子类。好莱坞原则别打电话给我们我们会打给你。四、命令模式Command一句话把一个请求封装成一个对象从而支持撤销、排队、日志等操作。生活中的例子餐厅点菜你对服务员说一份红烧肉。服务员写到单子上命令对象拿到厨房。厨师按单做菜。如果你说那道菜取消服务员撕掉那张单撤销。遥控器按钮遥控器上每个按钮都是一个命令——开灯、关灯、调亮度。按钮不知道灯怎么工作它只知道触发这个命令。Git commit每次 commit 是一个命令对象记录了做了什么改动。git revert就是执行这个命令的 undo。Excel 的 CtrlZ每个操作被记成命令对象按 CtrlZ 就是取出最近一个命令执行它的 undo。结构图四个角色Command接口统一execute()undo()Invoker 只认识它ConcreteCommand具体命令封装一次操作 它的撤销逻辑持有 Receiver 引用Invoker调用者管理命令历史栈不关心命令干了什么Receiver接收者真正执行操作的对象被命令持有代码java代码python代码五、状态模式State一句话允许对象在内部状态改变时改变它的行为看起来像换了一个类。生活中的例子红绿灯红灯状态只能停绿灯状态只能走黄灯状态要减速。灯的行为完全取决于当前状态到了时间自动切换。手机锁屏状态按电源键解锁解锁状态按电源键锁屏关机状态按电源键开机——同一个按钮不同状态做不同的事。电梯开门状态不能运行运行状态不能开门故障状态什么都不能做——每个状态有自己的规则。网购订单待支付→已支付→已发货→已收货。每个状态下能做的操作不同待支付可以取消已发货不能取消只能拒收。结构图三个角色State状态接口定义所有状态下可触发的行为Context 只认识它ConcreteState具体状态实现该状态允许的操作不允许的操作直接报错并负责切换到下一个状态Context上下文持有当前状态把行为委托给它自己不写 if-else代码java代码python代码六、责任链模式Chain of Responsibility一句话把请求沿着一条链传递每个节点自己决定处理还是传给下一个。生活中的例子公司请假审批请 1 天组长批请 3 天经理批请 7 天总监批超过 7 天找 CEO。你只需要提交申请系统自动沿链条往上传。客服系统用户问题先到机器人机器人答不了转人工人工客服搞不定转主管主管搞不定转经理。Java 异常处理异常从内层 catch 往外层抛直到有人处理。中间件管道一个 HTTP 请求经过鉴权→限流→日志→业务处理每一层都可以决定放行还是拦截。结构图代码实现java代码python代码七、迭代器模式Iterator一句话提供一种方法顺序访问集合中的元素而不暴露集合的内部表示。生活中的例子翻书你不需要知道书是怎么装订的只需要翻下一页就能看到所有内容。电视遥控器翻台按下一个频道就行不用管电视信号怎么存储的。音乐播放列表点下一首不用管歌曲是存在数组里还是链表里。结构图