observer 构造函数不应传 target,它仅负责响应通知,绑定由被观察者(如 subject)在 subscribe/observe 阶段完成,以保持解耦;否则会导致复用性差、循环调用或 this 混乱。

Observer 构造函数要不要传 target?
大多数手写实现里,Observer 本身不接收被观察对象(target)——它只负责响应通知。真正绑定关系发生在 subscribe() 或 observe() 阶段,由被观察者(比如 Subject)持有观察者列表。硬塞 target 进 Observer 构造函数,反而会让观察者依赖具体 subject 实例,破坏解耦。
常见错误现象:new Observer(subject) 后发现无法复用到另一个 subject2;或者在 observer 内部直接调 target.notify(),导致循环调用或 this 指向混乱。
- Observer 应是纯回调容器:只管执行
update()或传入的 handler - 绑定动作交给 subject:如
subject.addObserver(new Observer(handler)) - 如果需要访问 subject 状态,通过闭包捕获,而非构造时传入实例
notify() 里遍历 observers 时删元素会跳过下一个?
是的,边遍历边 splice() 或 delete 会导致索引错位,典型表现是:移除第 2 个 observer 后,原第 3 个永远收不到通知。
使用场景:observer 主动退订(unsubscribe())、subject 销毁前清理、异常 observer 自动剔除。
- 安全做法:反向遍历
for (let i = list.length - 1; i >= 0; i--) - 更推荐:先收集待移除项,
notify()结束后统一清理 - 现代写法可改用
Array.from(list)创建快照再遍历,避免原数组被改
JavaScript 中用 Proxy 实现自动订阅算不算 Observer 模式?
不算。Proxy 拦截的是属性读写行为,属于响应式(Reactivity)机制;Observer 模式强调显式的「注册 → 通知 → 响应」链路,核心是松耦合的对象协作。
性能影响明显:Proxy 的 get/set 拦截开销固定,而传统 Observer 只在 notify() 时才触发回调,无读写成本。
- Proxy 适合数据驱动视图(如 Vue 2/3 响应式),但 observer 列表不可见、不可控
- Observer 模式下,你能精确控制谁被通知、何时通知、是否异步(如
queueMicrotask()) - 混用风险:用 Proxy 触发
notify()可以,但别把 Proxy 当 Observer 本体
Java 的 java.util.Observer 已弃用,还能用吗?
能跑,但不建议新项目用。JDK 9 标记为 @Deprecated(forRemoval = true),根本原因是它强制 observer 继承 java.util.Observer 类,违反组合优于继承原则,且 notify 逻辑锁死在 setChanged() + notifyObservers() 流程里,没法自定义事件类型或参数。
替代方案更灵活:java.util.concurrent.CopyOnWriteArrayList 存 observer,配合泛型接口 interface Observer<t></t> 和事件对象 Event。
- 旧代码迁移时,注意
update(Observable, Object)的第二个参数类型丢失问题 - Android 开发中
LiveData或Flow是更现代的替代,但它们解决的是生命周期感知问题,不是纯 Observer 模式 - 如果只是简单通知,一个
Consumer<string></string>加ArrayList就够用,别为模式而模式
事情说清了就结束。真正在意解耦和可测性时,observer 的「注册时机」「通知边界」「错误隔离」比语法糖重要得多。










