观察者模式的核心是通过Subject和Observer两个接口实现解耦:Subject提供attach、detach、notify方法,Observer定义update方法,双方仅依赖接口而非具体实现。

用接口实现观察者模式,核心不是写一堆类,而是靠两个契约接口把主题和观察者“隔开”——主题只认 Subject 接口,观察者只认 Observer 接口。只要双方遵守接口约定,谁改状态、谁响应更新、怎么通知、怎么处理,彼此完全不关心。解耦就落在这个“契约”上。
定义两个接口:一个管通知,一个管响应
必须先明确两个角色的最小能力边界:
-
Subject 接口:只暴露三个动作——
attach()(加观察者)、detach()(删观察者)、notify()(发通知)。它不暴露内部状态字段,也不指定通知内容格式,只保证“有变动就喊一声”。 -
Observer 接口:只定义一个入口方法
update(Subject s)。参数传入的是 Subject 接口类型,不是具体实现类。这样观察者只能通过接口提供的 getter(如s.getState())获取数据,无法直接读私有字段,也避免强依赖。
主题实现要封装状态 + 自动触发通知
ConcreteSubject 不是简单存个值,而是把“状态变更”和“通知动作”绑死在 setter 里:
- 状态字段设为
private,比如private String status; - 提供 public setter,例如
public void setStatus(String s),里面先赋值,再立刻调用this.notify(); - notify() 方法遍历观察者列表,逐个调用
observer.update(this)—— 注意传的是this(Subject 接口实例),不是 this.getClass()。 - 不提供“手动通知”或“批量设置后统一通知”的入口,防止业务代码绕过机制漏通知。
观察者实现要专注自身逻辑,不反向操作主题
每个 ConcreteObserver 的 update() 方法只做三件事:
- 从参数
Subject s中安全取值,例如String newState = s.getState(); - 基于新值执行本职工作:刷新 UI、写日志、发消息、更新缓存……
- 绝不调用
s.setState(...)或任何可能触发二次通知的操作,否则容易形成循环调用。
如果某个观察者需要异步处理(比如网络请求),应在 update 内启动新线程或协程,而不是阻塞通知链。
注册与解绑必须严格配对,尤其注意生命周期
接口本身不解决内存泄漏,但接口设计强制你意识到“谁负责清理”:
- 注册发生在初始化阶段(如 Activity.onCreate、Vue.onMounted),调用
subject.attach(this); - 解绑必须在销毁前执行(如 Activity.onDestroy、Vue.onUnmounted),调用
subject.detach(this); - 若使用匿名内部类或 Lambda 做观察者,需确保能准确传入同一实例用于 detach,否则 remove 失效;
- 临时监听可封装成 AutoDetachObserver:update 执行完自动调用 detach,适合一次性的回调场景。










