什么是观察者模式?

观察者模式定义了一种一对多的依赖关系,让多个观察者对象(Observer)同时监听某一个主题对象(Subject)。当这个主题对象的状态发生变化时,它会自动通知所有依赖于它的观察者对象,使它们能够自动更新自己。
想象一下你在 B 站关注了某个 UP 主(主题对象 Subject)。当这位 UP 主发布了新视频(状态变化),B 站平台(通常也是 Subject 的一部分或由其触发)会自动向所有关注他的粉丝(观察者 Observer)发送通知。你收到通知后,就可以选择去观看新视频(观察者的更新动作)。UP 主不需要知道每个粉丝具体是谁,只需要通过平台通知所有订阅者即可。
💡
观察者模式的核心在于解耦状态变更方(Subject)和状态响应方(Observer)

核心思想

观察者模式的核心思想在于 分离关注点 和 实现松耦合
  1. 依赖倒置: 主题(被观察者)不直接依赖于具体的观察者,而是依赖于一个抽象的观察者接口。同样,观察者也不必了解主题的内部细节,只需知道如何响应通知。
  1. 发布-订阅机制: 主题维护一个订阅者(观察者)列表。当其状态发生变化时,它会遍历这个列表,并“发布”通知给所有订阅者。观察者则“订阅”主题,并在接收到通知时执行相应的更新操作。
  1. 变动的主动通知: 取代了观察者不断轮询检查主题状态的方式,由主题在状态变更时主动推送通知,提高了效率并降低了耦合度。
💡
简单来说,就是“你变了,快告诉我,我好跟着变,但我不需要知道你具体怎么变的,你也不需要知道我是谁,我们只通过约定的方式(接口)沟通”。

核心角色

  1. Subject(主题/被观察者接口): 定义了添加、删除和通知观察者对象的方法。
  1. Observer(观察者接口): 定义了一个更新接口,用于在接收到主题状态变更通知时进行更新。
  1. ConcreteSubject(具体主题/具体被观察者): 实现了主题接口。它维护自身的状态,并在状态改变时向已注册的观察者发送通知。通常会包含一个观察者列表。
  1. ConcreteObserver(具体观察者): 实现了观察者接口。它维护一个指向具体主题对象的引用(可选,取决于推模型还是拉模型),并存储需要与主题状态同步的状态。当接收到更新通知时,执行具体的更新逻辑。

核心步骤

  1. 定义接口: 模式中通常包含两个核心角色:Subject(主题)和 Observer(观察者)。它们通常被定义为接口或抽象类。
      • Subject 接口定义了注册(attach/subscribe)、注销(detach/unsubscribe)和通知(notify)观察者的方法。
      • Observer 接口定义了一个更新(update)方法,供 Subject 在状态变化时调用。
  1. 维护列表: ConcreteSubject(具体主题)实现了 Subject 接口。它内部维护了一个 Observer 对象的列表,并持有具体的状态。
  1. 状态变化与通知: 当 ConcreteSubject 的状态发生改变时,它会调用自己的 notify 方法。
  1. 遍历与更新: notify 方法会遍历内部维护的 Observer 列表,并调用每个 Observer 的 update 方法,将状态变更的信息传递给它们(可以是推送具体数据,也可以仅仅是一个通知信号让 Observer 自己去拉取)。
  1. 响应变化: ConcreteObserver(具体观察者)实现了 Observer 接口。在 update 方法中,它会根据接收到的信息或信号,执行相应的逻辑来响应 Subject 的状态变化。

优点

  1. 松耦合 (Loose Coupling): Subject 和 Observer 之间是松耦合的。Subject 只知道 Observer 实现了特定接口,不需要了解具体是哪个类、有多少个 Observer。同样,Observer 可以在不了解 Subject 内部细节的情况下对其状态变化做出反应。
  1. 广播通信 (Broadcast Communication): Subject 可以向任意数量的 Observer 广播通知,无需单独指定接收者。
  1. 易于扩展 (Extensibility): 可以很方便地增加新的 ConcreteObserver 类,而无需修改 Subject 或其他 Observer 的代码,符合开闭原则。同样,也可以引入新的 ConcreteSubject。
  1. 清晰的职责分离: Subject 专注于维护状态和通知,Observer 专注于响应状态变化

可能的缺点

  1. 通知顺序问题: 如果 Observer 之间存在依赖关系,通知的顺序可能会导致问题,因为 Subject 通常不保证通知的顺序。
  1. 性能开销: 如果 Observer 数量过多,或者 Observer 的 update 方法逻辑过于复杂,通知过程可能会比较耗时,影响性能。
  1. 意外更新 (Unexpected Updates): 如果一个 Subject 的状态频繁变化,或者一个 Observer 订阅了多个 Subject,可能会导致一系列连锁更新,有时难以调试和控制。
  1. 内存泄漏风险 (Memory Leaks): 在某些场景下(尤其是在有生命周期的组件如 Android Activity/Fragment 中),如果 Observer 没有在不再需要时从 Subject 注销(unsubscribe/detach),Subject 会持有对 Observer 的引用,可能导致 Observer 无法被垃圾回收,造成内存泄漏。

使用场景

  1. 当一个对象的改变需要同时通知其他多个对象,而你不想让这个对象与那些被通知的对象形成紧耦合。
  1. 当一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这两者封装在独立的对象中以允许它们各自独立地改变和复用。
  1. 事件驱动系统:如 GUI 组件的事件监听(点击按钮通知监听器)。
  1. 发布/订阅系统。
  1. Android 中的场景:
      • LiveData: Android Jetpack 中的 LiveData 是观察者模式的典型实现。Activity/Fragment (作为 LifecycleOwner) 观察 LiveData 对象,当 LiveData 的数据变化时,它会自动通知活跃的观察者更新 UI。LiveData 还具备生命周期感知能力,能有效避免内存泄漏。
      • StateFlow / SharedFlow: Kotlin Coroutines 中的 StateFlow 和 SharedFlow 也扮演着类似的角色,提供了更现代、更灵活的响应式编程方式,常用于 MVVM/MVI 架构中 ViewModel 向 UI 层暴露状态。
      • BroadcastReceiver: Android 系统级的广播机制,允许应用监听系统事件(如网络变化、电量变化)或其他应用的广播,这也是观察者模式的一种体现(系统/其他应用是 Subject,BroadcastReceiver 是 Observer)。
      • 事件总线 (Event Bus): 像 GreenRobot EventBus 或 Square Otto (已不推荐) 这样的库,允许应用内组件之间进行解耦的通信,也是基于观察者模式(发布者-订阅者)。
      • RecyclerView.Adapter.notifyDataSetChanged() / notifyItemChanged() 等: 虽然不是严格意义上的 Subject-Observer 接口实现,但概念上类似。Adapter 数据变化后(Subject 状态变化),调用 notify... 方法通知 RecyclerView(作为 Observer)更新视图。
      • UI 事件监听: Button.setOnClickListener { ... },这里的 Button 是 Subject(事件源),Lambda 表达式或 OnClickListener 实例是 Observer。

UML图

下面是一个观察者模式的 UML 结构图,以我们稍后将要实现的“新闻订阅”为例:
  • NewsObserver (Observer Interface): 定义了所有具体观察者(如 PhoneAppObserver, WebClientObserver)必须实现的 update 方法。
  • NewsAgencySubject (Subject Interface): 定义了主题(如 ConcreteNewsAgency)需要提供的注册 (subscribe)、注销 (unsubscribe) 和通知 (notifySubscribers) 方法。
  • ConcreteNewsAgency (Concrete Subject):
    • 实现了 NewsAgencySubject 接口。
    • 内部持有 subscribers 列表(List<NewsObserver>),这就是它 maintains subscribers 的关系 (o--> "*" NewsObserver)。这里的 o-- 表示聚合关系(Subject 拥有 Observer,但 Observer 生命周期独立),"*" 表示可以有零个或多个 Observer。
    • 当 publishNews 被调用并改变状态(latestNews)后,它会调用 notifySubscribers。
    • notifySubscribers 方法会遍历 subscribers 列表,并调用每个 NewsObserver 的 update 方法,这就是 notifies 的关系 (..> NewsObserver),虚线箭头表示依赖或调用关系。
  • PhoneAppObserver / WebClientObserver (Concrete Observers):
    • 都实现了 NewsObserver 接口。
    • 在 update 方法中定义了收到新闻后的具体行为(打印到控制台)。
  • Client (使用者): 代表了 main 函数中的代码。它创建了 ConcreteNewsAgency 和各种 NewsObserver 的实例,并将 Observer 注册到 Subject 中,最后触发 Subject 的状态变化 (publishNews)。它 uses 这些类来完成整个流程。

代码示例

在这个例子中:
  • ConcreteNewsAgency 是 Subject,负责发布新闻和管理订阅者列表。
  • PhoneAppObserver 和 WebClientObserver 是 Observer,它们实现了 update 方法来响应新闻发布。
  • main 函数演示了如何创建 Subject 和 Observer,如何进行订阅和取消订阅,以及 Subject 状态变化时如何自动通知所有当前订阅的 Observer。

总结

  • 观察者模式是一种强大的行为型模式,它实现了当一个对象状态改变时,所有依赖它的对象都能得到自动通知和更新
  • 核心在于解耦 Subject 和 Observer,使得它们可以独立变化和扩展。
  • 在 Android 开发中,LiveData 和 Flow 是观察者模式的现代应用典范,极大地简化了 UI 和数据状态之间的同步问题,并帮助管理生命周期,防止内存泄漏。
💡
记住它的核心:一个状态改变(Subject),多个自动响应(Observers),中间靠接口(Subject/Observer Interface)和列表(Subject 内部维护)解耦。
 
设计模式——行为型-模版方法模式设计模式——结构型-装饰器模式
Loading...