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
- 大型事件系统 → 结合
LiveData
或Flow
- 需要生命周期感知 → 推荐 Android 架构组件中的
LifecycleOwner
+Observer
📌 补充:在 Android 开发中,更推荐使用
LiveData
或Kotlin Flow
来实现响应式数据流,它们内置了生命周期管理,避免内存泄漏问题。
本文代码已上传至 GitHub 示例仓库:https://github.com/example/kotlin-design-patterns