如何避免观察者模式坑
观察者模式(Observer Pattern)用起来很爽,但坑也不少。下面从常见坑点 → 原因 → 规避方式系统讲一遍,基本能覆盖 90% 的实际问题。
一、最常见的 8 个坑
1️⃣ 内存泄漏(最经典)
现象
- 观察者注销失败
- 对象已经不用了,却还被通知
- 内存一直涨
原因
- 被观察者持有观察者的强引用
- 忘记
unsubscribe / removeObserver
规避
// 典型错误
subject.addObserver(this);
// 页面销毁时忘了 remove
✅ 正确做法:
- 生命周期绑定
- 使用
WeakReference - 框架自动管理(RxJava、LiveData、Flow)
@Override
protected void onDestroy() {
subject.removeObserver(this);
}
2️⃣ 观察者顺序不可控
现象
- 通知顺序和注册顺序不一致
- 不同环境表现不同
原因
- 使用
HashSet / HashMap存观察者
规避
- 明确顺序需求
- 使用
List / CopyOnWriteArrayList
List observers = new CopyOnWriteArrayList<>();
3️⃣ 观察者里做耗时操作
现象
- 通知卡顿
- UI 卡死
- 被观察者线程被拖慢
原因
- 观察者直接做 IO / 计算
规避
- 观察者只做“响应”
- 耗时逻辑异步处理
observer.onEvent(e -> {
asyncRun(() -> handle(e));
});
4️⃣ 通知过程中修改观察者列表
现象
ConcurrentModificationException- 漏通知 / 重复通知
原因
- 在
onNext()里 add / remove observer
规避
- 遍历副本
- 使用并发容器
for (Observer o : new ArrayList<>(observers)) {
o.update();
}
5️⃣ 观察者抛异常,影响其他人
现象
- 一个观察者崩了
- 后面观察者全收不到通知
规避
- 统一 catch
- 或设计容错策略
try {
observer.update();
} catch (Exception e) {
log.error("observer error", e);
}
6️⃣ 观察者和被观察者强耦合
现象
- 观察者依赖具体实现
- 无法复用、无法单测
原因
- 直接依赖具体类
规避
- 面向接口
- 事件对象解耦
interface Observer {
void onEvent(Event e);
}
7️⃣ 事件爆炸(通知太频繁)
现象
- 高频更新(如滚动、动画)
- 性能急剧下降
规避
- 合并事件(throttle / debounce)
- 控制通知粒度
8️⃣ 滥用观察者模式
现象
- 所有模块都用观察者
- 逻辑跳转混乱,调试困难
规避
- 不是所有“通知”都该用观察者
- 优先用:
- 直接调用
- 回调
- 状态管理(ViewModel / Store)
二、设计层面如何提前避坑
✅ 1. 明确角色边界
- Subject:只负责通知
- Observer:只负责响应
- 不做业务编排
✅ 2. 区分「事件」和「状态」
- 事件:发生一次
- 状态:一直存在
| 场景 | 建议 |
|---|---|
| UI 状态 | LiveData / StateFlow |
| 一次性事件 | EventBus / 回调 |
| 复杂业务 | 状态机 / Redux |
✅ 3. 使用成熟实现,别自己造轮子
| 场景 | 推荐 |
|---|---|
| Android | LiveData / StateFlow |
| Java | RxJava |
| JS | RxJS |
| 后端 | Guava EventBus |
三、一个“安全版”观察者模板
class SafeSubject {
private final List observers = new CopyOnWriteArrayList<>();
void add(Observer o) {
observers.add(o);
}
void remove(Observer o) {
observers.remove(o);
}
void notify(Event e) {
for (Observer o : observers) {
try {
o.onEvent(e);
} catch (Exception ex) {
log.error(ex);
}
}
}
}
四、什么时候不该用观察者
❌ 调用链很短
❌ 逻辑必须同步
❌ 强顺序依赖
❌ 调用者和被调用者天然强关联
五、一句话总结
观察者模式最大的坑不是模式本身,而是“通知谁、什么时候通知、什么时候取消、谁负责生命周期”。
如果你愿意,我可以:
- 帮你 review 一段观察者代码
- 或对比 观察者 vs 发布订阅 vs 回调
- 或结合 Android / 后端 / 前端 给你具体建议