1. 简介

本文将介绍观察者模式(Observer Pattern),并通过 Kotlin 实现几种不同的变体。该模式在事件驱动系统中非常常见,尤其适合解耦状态变化与响应逻辑。

如果你熟悉 Java 的 Observer 模式,会发现 Kotlin 提供了更简洁的实现方式。我们不仅会复刻经典设计,还会利用 Kotlin 特性写出更现代、更安全的代码。

2. 什么是观察者模式?

观察者模式是一种行为型设计模式,最早由 Gang Of Four 在《设计模式》一书中提出。

✅ 核心思想是:
定义一种订阅机制,使得多个对象(观察者)可以监听某个主体对象(被观察者)的状态变化,并在状态变更时自动收到通知。

这种模式广泛应用于 GUI 组件更新、消息总线、事件系统等场景。

举个例子:假设你订阅了 Baeldung 的技术文章推送。每当有新文章发布,所有订阅者都会收到通知。
在这个模型中:

  • Baeldung 官网Observable(被观察者)
  • 读者Observer(观察者)

⚠️ 注意:这里的“订阅”不一定是网络请求意义上的“接口”,而是程序内部的一种事件监听机制。

3. 经典 GoF 风格实现

我们先从最原始的方式入手——模仿 Gang Of Four 的标准接口设计。

3.1 定义接口

首先定义两个核心接口:

interface IObserver {
    fun update()
}

update() 方法会在被观察对象状态变化时触发。

接着是被观察者的接口:

interface IObservable {
    val observers: ArrayList<IObserver>

    fun add(observer: IObserver) {
        observers.add(observer)
    }

    fun remove(observer: IObserver) {
        observers.remove(observer)
    }

    fun sendUpdateEvent() {
        observers.forEach { it.update() }
    }
}

📌 要点说明:

  • 使用 ArrayList 存储观察者列表(实际项目建议用 CopyOnWriteArrayList 避免并发修改异常)
  • add / remove 用于管理订阅关系
  • sendUpdateEvent 广播通知给所有观察者

3.2 实现被观察者:一个简单的新闻订阅系统

我们现在来构建一个模拟 Baeldung 文章发布的类:

class BaeldungNewsletter : IObservable {
    override val observers: ArrayList<IObserver> = ArrayList()
    var newestArticleUrl = ""
        set(value) {
            field = value
            sendUpdateEvent()
        }
}

关键点在于对 newestArticleUrl 的 setter 进行拦截:一旦 URL 更新,立即触发通知。

💡 踩坑提醒:如果不重写 setter,手动调用 sendUpdateEvent() 容易遗漏,造成状态不同步。

3.3 实现观察者

观察者的实现很简单,只需要实现 update() 方法即可:

class BaeldungReader(private var newsletter: BaeldungNewsletter) : IObserver {
    override fun update() {
        println("New Baeldung article: ${newsletter.newestArticleUrl}")
    }
}

使用示例:

fun main() {
    val newsletter = BaeldungNewsletter()
    val reader1 = BaeldungReader(newsletter)
    val reader2 = BaeldungReader(newsletter)

    newsletter.add(reader1)
    newsletter.add(reader2)

    newsletter.newestArticleUrl = "https://www.baeldung.com/kotlin-observer-pattern"
}

输出:

New Baeldung article: https://www.baeldung.com/kotlin-observer-pattern
New Baeldung article: https://www.baeldung.com/kotlin-observer-pattern

❌ 缺点分析:

  • 强依赖具体类(BaeldungReader 必须持有 newsletter 引用)
  • 观察者只能获取最新状态,无法得知变化细节
  • 手动维护订阅列表,容易内存泄漏(未及时 remove)

4. 使用 Kotlin 内置 Observable 委托

Kotlin 提供了语言级支持的属性委托机制,其中 Delegates.observable 可以极大简化观察者模式的实现。

4.1 基本语法回顾

var observedProperty: String by Delegates.observable(initialValue) { property, oldValue, newValue ->
    // 当属性被赋值时执行此 lambda
}

参数含义:

  • property: 被观察的 KProperty 对象(通常不用)
  • oldValue: 旧值
  • newValue: 新值

4.2 改造新闻系统

我们不再需要 IObservable 接口,直接用委托实现:

class BaeldungNewsletter {
    val newestArticleObservers = mutableListOf<(String) -> Unit>()

    var newestArticleUrl: String by Delegates.observable("") { _, _, newValue ->
        newestArticleObservers.forEach { it(newValue) }
    }
}

观察者通过 lambda 注册:

val newsletter = BaeldungNewsletter()

// 添加观察者
newsletter.newestArticleObservers.add { newestArticleUrl ->
    println("New Baeldung article: $newestArticleUrl")
}

// 触发更新
newsletter.newestArticleUrl = "https://www.baeldung.com/kotlin-delegated-properties"

✅ 优势:

  • 更函数式,代码更简洁
  • 不需要继承接口,降低耦合
  • 自动处理属性变更通知

⚠️ 局限性:

  • 仅适用于单个属性的监听
  • 多属性联动需额外封装
  • 不支持异步通知(默认同步执行)

5. 总结

方式 适用场景 是否推荐
GoF 接口模式 复杂状态管理、多属性联动 ⚠️ 仅作教学理解
Kotlin 委托模式 单属性监听、轻量级通知 ✅ 日常开发首选

📌 最佳实践建议:

  • 小范围状态同步 → 使用 Delegates.observable
  • 大型事件系统 → 结合 LiveDataFlow
  • 需要生命周期感知 → 推荐 Android 架构组件中的 LifecycleOwner + Observer

📌 补充:在 Android 开发中,更推荐使用 LiveDataKotlin Flow 来实现响应式数据流,它们内置了生命周期管理,避免内存泄漏问题。

本文代码已上传至 GitHub 示例仓库:https://github.com/example/kotlin-design-patterns


原始标题:The Observer Pattern in Kotlin