← 返回题目列表

观察者模式有哪些角色?通知流程是什么?

高频 简单 第 1 / 27 题 更新于 2026/07/28
观察者模式SubjectObserver通知流程

简化版

观察者模式主要有 Subject、Observer、ConcreteSubject、ConcreteObserver 四类角色。流程是观察者先订阅主题,主题状态变化后遍历观察者列表并调用通知方法,观察者收到事件后执行自己的处理逻辑。

详细版

观察者模式的角色可以这样拆:

  1. Subject:主题接口,提供注册、移除、通知观察者的方法。
  2. ConcreteSubject:具体主题,保存状态和观察者列表,在状态变化时通知。
  3. Observer:观察者接口,定义统一的更新方法。
  4. ConcreteObserver:具体观察者,收到通知后执行具体动作。

典型通知流程:

  1. 观察者调用 attach 注册到主题;
  2. 主题内部维护观察者集合;
  3. 主题状态发生变化;
  4. 主题调用 notifyObservers
  5. 每个观察者执行自己的 update 方法。

在工程项目中,主题不一定手写观察者列表,也可能由 Spring 容器、事件总线或消息队列维护订阅关系。

完整版教学

一、Subject 是事件源

Subject 可以理解为“事件源”或“被观察者”。它负责维护观察者关系,并在状态变化时发出通知。

一个简化接口可以这样写:

public interface Subject {
    void attach(Observer observer);
    void detach(Observer observer);
    void notifyObservers(Event event);
}

这里的重点不是方法名,而是职责:Subject 管理订阅关系并触发通知。

在实际项目里,Subject 可能不是一个传统对象,而是事件发布器,例如:

  • ApplicationEventPublisher
  • EventBus;
  • 消息队列 Producer;
  • 前端事件中心。

二、Observer 是统一响应接口

Observer 定义观察者收到通知后应该执行什么方法。

public interface Observer {
    void update(Event event);
}

所有具体观察者都实现这个接口。主题只依赖 Observer 抽象,不依赖短信观察者、积分观察者、日志观察者这些具体类。

这就是观察者模式的解耦点。

三、ConcreteObserver 承载具体动作

具体观察者只处理自己关心的逻辑。

public class SmsObserver implements Observer {
    public void update(Event event) {
        // 发送短信
    }
}

public class CouponObserver implements Observer {
    public void update(Event event) {
        // 发放优惠券
    }
}

每个观察者应该尽量职责单一。如果一个观察者里又写了短信、积分、画像、日志,观察者本身就变成新的上帝类了。

四、通知流程的关键是“订阅在前,通知在后”

观察者模式不是凭空知道谁要响应事件。必须先建立订阅关系。

注册观察者
  -> 保存到观察者列表
  -> 主题状态变化
  -> 遍历观察者
  -> 调用 update

如果观察者没有注册,主题通知时就不会触发它。这也是线上排查时要注意的点:事件没有被处理,不一定是事件没发,也可能是观察者没注册成功。

五、事件对象让通知更稳定

观察者的 update 方法可以只传一个事件对象,而不是传一堆零散参数。

public class OrderPaidEvent {
    private Long orderId;
    private Long userId;
    private BigDecimal amount;
    private LocalDateTime paidAt;
}

这样有几个好处:

  • 事件语义清晰;
  • 后续扩展字段更方便;
  • 观察者可以只取自己需要的字段;
  • 日志和排查更容易。

六、Subject、Observer 与事件对象的协作

Subject 当前注册 3 个观察者,notify 时通常遍历 3 次;移除 1 个后下一次只应通知剩余 2 个。

register -> snapshot observers -> notify(event) -> each observer

这个推演把模式带来的收益和成本放在同一条调用链上。设计评审时既要确认结果正确,也要确认额外层次没有改变原有契约。

七、边界、代价与验证

检查维度应确认的内容
正确性事件对象应表达已经发生的事实,并携带处理所需的最小稳定信息。
适用边界通知时直接遍历可变列表可能遇到监听器自移除导致并发修改;复制快照或并发容器更稳妥。
测试证据覆盖正常路径、异常路径、空值或未知类型,并断言调用次数与顺序
运行成本记录额外对象、调用层数、延迟、线程或内存开销,不用模式名称代替测量

模式落地后要用单元测试验证独立职责,用集成测试验证对象装配和真实调用入口。涉及并发、远程或异步时,还要补充竞态、超时、重复执行与资源释放测试。

易错点:事件对象应表达已经发生的事实,并携带处理所需的最小稳定信息。

八、常见误区与追问

  • 误区:Subject 只负责保存观察者,不负责发通知。 维护订阅和在恰当时机通知都属于 Subject 协作职责,具体存储可委托事件总线。
  • 误区:发布事件天然等于异步和最终一致性。 观察者只定义一对多通知关系;同步还是异步、是否跨进程以及一致性语义都由具体实现决定。
  • 误区:示例代码能运行,就代表模式边界设计正确。 能运行只证明一条路径,仍要检查异常、并发、顺序、生命周期以及新增实现时是否修改稳定代码。
  • 追问:通知顺序是观察者模式保证的吗? 不是;除非具体实现定义排序,否则观察者不应依赖注册顺序。
  • 追问:观察者失败后由谁重试? 模式本身没有答案;同步场景要定义异常传播,异步场景要由执行器、事件总线或消息系统提供重试与死信策略。
  • 追问:怎样验证这套设计没有改变原有语义? 先做基线行为测试,再对新增层验证返回值、异常类型、调用次数和顺序;性能敏感处还要比较改造前后的量化指标。

九、加强记忆

观察者模式的角色记成“主题管订阅,观察者管响应”。通知流程是先注册,再状态变化,最后主题遍历观察者并调用统一方法;事件对象负责携带上下文数据,让观察者之间保持解耦。