行为型模式深度解析
前端最核心的行为型模式:观察者 vs 发布订阅(Vue 响应式的底层)、策略模式(替代 if/else 地狱)。
一句话概括
行为型模式解决的是”对象之间怎么通信、职责怎么分配“的问题。前端最核心的两个是:观察者模式(被观察者变化时自动通知依赖它的观察者,是 Vue 响应式的思想基础)和策略模式(把可互换的算法封装成独立策略,替代 if/else 地狱)。还有一个必须分清的是发布订阅模式(观察者的变体,通过中间的事件中心彻底解耦)。这些模式是前端框架响应式系统、事件系统、插件机制的底层支柱。
核心知识点
1. 观察者模式:Vue 响应式的思想基础
观察者模式 = 被观察者(Subject)维护一组观察者(Observer),自己变化时逐个通知。核心是”依赖收集 + 变更通知”:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Subject {
#observers = new Set();
subscribe(fn) { this.#observers.add(fn); return () => this.#observers.delete(fn); }
unsubscribe(fn) { this.#observers.delete(fn); }
notify(data) { this.#observers.forEach(fn => fn(data)); }
}
const store = new Subject();
const unsub = store.subscribe(data => console.log('更新了', data));
store.notify({ count: 1 }); // 打印 "更新了 {count:1}"
unsub(); // 取消订阅
// 这正是 Vue 响应式的简化模型:
// data 变化 → 通知所有依赖它的组件(观察者)→ 组件重新渲染
要点:观察者直接被 Subject 持有,是”一对多”的直接通知关系。Vue 的 dep → watcher、Redux 的 store.subscribe 都是这个模式。
2. 发布订阅模式:加一个中间层,彻底解耦
发布订阅和观察者最大的区别:多了一个事件中心(EventBus),发布者和订阅者互不相识,都只认识事件中心:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class EventBus {
#events = new Map();
on(type, fn) {
if (!this.#events.has(type)) this.#events.set(type, new Set());
this.#events.get(type).add(fn);
}
off(type, fn) { this.#events.get(type)?.delete(fn); }
emit(type, ...args) { this.#events.get(type)?.forEach(fn => fn(...args)); }
}
const bus = new EventBus();
bus.on('user:login', user => console.log('记录日志', user));
bus.on('user:login', user => console.log('更新 UI', user));
bus.emit('user:login', { id: 1 }); // 两个订阅者都被触发,发布者不知道有谁
对比记忆:观察者 = 直接通知(Subject 认识 Observer);发布订阅 = 间接通知(双方都只认识中间人)。DOM 的 addEventListener、Node 的 EventEmitter、跨组件通信的 EventBus 都是发布订阅。
3. 策略模式:消灭 if/else 地狱
策略模式把一组可互换的算法封装成独立对象,运行时按需选择,替代层层嵌套的条件判断:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// ❌ if/else 地狱:加一种支付方式就要加一个分支,函数越来越长
function pay(type, amount) {
if (type === 'alipay') { /* 支付宝逻辑 */ }
else if (type === 'wechat') { /* 微信逻辑 */ }
else if (type === 'card') { /* 银行卡逻辑 */ }
// ...越来越多
}
// ✅ 策略模式:每种算法一个独立策略,通过 map 查找
const strategies = {
alipay: (amount) => console.log('支付宝支付', amount),
wechat: (amount) => console.log('微信支付', amount),
card: (amount) => console.log('银行卡支付', amount),
};
function pay(type, amount) {
const strategy = strategies[type];
if (!strategy) throw new Error('不支持的支付方式');
return strategy(amount);
}
收益:新增策略只需加一个 key,不改原函数(开闭原则);每个策略可独立测试;消除深层嵌套。适合”同一操作有多种实现、且会持续扩展”的场景(支付、校验规则、折扣计算、排序算法)。
4. 观察者 vs 发布订阅:面试必问辨析
| 维度 | 观察者模式 | 发布订阅模式 |
|---|---|---|
| 耦合 | Subject 直接持有 Observer | 双方都只认识事件中心 |
| 通知方式 | 被观察者直接逐个通知 | 通过中间人广播 |
| 关系感知 | 观察者知道”谁在通知我” | 双方互不相识 |
| 典型例子 | Vue 响应式、Redux subscribe | EventBus、EventEmitter |
一句话:发布订阅 = 观察者 + 事件中心(解耦中介)。观察者是”老师点名通知学生”,发布订阅是”大喇叭广播,谁在听谁响应”。
5. 策略模式 vs 状态模式
常被混淆的一对。关键看“换算法”是谁驱动的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 策略模式:外部主动选择策略,策略之间互不感知
pay('alipay', 100); // 调用方决定用哪个
// 状态模式:对象内部状态自动切换行为,状态间有流转关系
class Order {
#state = 'created'; // 状态:created → paid → shipped
next() {
const transitions = {
created: () => { this.#state = 'paid'; console.log('已支付'); },
paid: () => { this.#state = 'shipped'; console.log('已发货'); },
shipped: () => console.log('已到最终状态'),
};
transitions[this.#state]?.();
}
}
一句话:策略 = 外部选(被动),状态 = 内部转(主动流转)。状态模式适合”状态机”(订单、审批流、播放器状态),策略模式适合”算法切换”(支付、校验)。
其实你每天都在用
- Vue 响应式更新:你
this.count++,视图自动更新,底层就是 dep 通知 watcher(观察者)重新渲染 - DOM 事件监听:
addEventListener('click', fn),事件触发时浏览器广播给所有监听者,就是发布订阅 - 跨组件通信的 EventBus:A 组件
bus.emit('refresh'),B 组件bus.on('refresh')收到,双方互不 import,就是发布订阅 - 支付方式选择:结算页选支付宝/微信/银行卡,背后是一套策略 map 按 key 分发,就是策略模式
- 表单校验规则:邮箱、手机号、身份证各有独立的校验函数,按字段类型选对应校验器,就是策略模式
常见误解(FAQ)
❌ 误区一:”观察者模式和发布订阅模式是一回事,只是叫法不同”
这是面试最高频的失分点。区别在有没有中间事件中心:观察者是 Subject 直接持有并通知 Observer(强关联、感知对方);发布订阅是双方只认识事件中心、互不相识(彻底解耦)。Vue 响应式是观察者,EventBus/EventEmitter 是发布订阅,别混为一谈。
❌ 误区二:”前端应该多用 EventBus,全局通信方便”
EventBus 是隐式全局耦合——事件满天飞,谁发的、谁收的、什么时候触发,全靠字符串约定,重构时极易漏改、难以追踪,出了 bug 没法溯源。它适合简单、低频、跨层级的通信;复杂业务应该用状态管理(store)或组件通信,而不是把 EventBus 当万能胶水。
❌ 误区三:”策略模式就是拿 Map 存函数,本质还是 if/else”
区别在扩展性和开闭原则:if/else 加分支要改原函数(改一处、冒风险),策略模式加分支只需加一个 key(原函数不动)。而且策略可以被独立测试、独立复用、运行时动态注册(比如插件系统),这是 if/else 做不到的。Map 只是实现载体,真正的价值是”策略可插拔”。
❌ 误区四:”行为型模式都是后端/框架的事,业务代码用不上”
行为型模式是业务代码解耦最直接的武器:复杂表单的校验规则是策略、订单状态流转是状态模式、模块间通信是发布订阅、埋点上报是观察者。前端业务越复杂,这些模式越能帮你把”一团 if/else 和事件乱飞”整理成”可扩展、可测试、可维护”的结构。
一句话总结
行为型模式解决的是代码里的”怎么通知、怎么选择”——观察者让依赖自动感知变化、发布订阅用中间人彻底解耦、策略模式让算法可插拔,掌握了它们,你写的就不再是”一坨能跑的 if/else”,而是”一套能生长的结构”。