重修设计模式-行为型-观察者模式
Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.
在对象之间订阅一个一对多的依赖,当一个对象状态改变时,它的所有依赖对象都会自动收到通知。
观察者模式(Observer Pattern)也被称为发布订阅模式(Publish-Subscribe Pattern),主要用于在对象之间建立一种一对多的依赖关系,当一个对象的状态发生变化时,能够自动通知所有依赖于它的对象,并让它们同步更新。观察者和被观察者之间一般通过抽象接口交互,耦合性低,扩展方便。
观察者(Observable)和被观察者(Observer)的称呼比价灵活,比如:Subject-Observer、Publisher-Subscriber、Producer-Consumer、Dispatcher-Listener 等,但只要符合上面的定义都可以看做是观察者模式。
观察者模式角色
- 主题(Subject):被依赖的对象(1)。维护一个观察者列表,在状态发生变化时通知所有观察者。
- 观察者(Observer):依赖的对象(N)。提供一个更新接口,在主题状态更新时会收到通知,处理相应逻辑。
观察者模式是一个比较抽象的模式,不同场景有不同实现,最经典的实现方式如下:
interface Subject<T> {
fun register(observer: Observer<T>)
fun remove(observer: Observer<T>)
fun notify(msg: T)
}
interface Observer<T> {
fun update(data: String)
}
class SubjectImpl: Subject<String> {
private val mList = mutableListOf<Observer<String>>()
override fun register(observer: Observer<String>) {
mList.add(observer)
}
override fun remove(observer: Observer<String>) {
mList.remove(observer)
}
override fun notify(msg: String) {
mList.forEach { it.update(msg) }
}
}
fun main() {
val subject = SubjectImpl()
subject.register(object: Observer<String> {
override fun update(data: String) { println("observer 1 update:${data}") }
})
subject.register(object: Observer<String> {
override fun update(data: String) { println("observer 2 update:${data}") }
})
subject.notify("消息变更数据...")
}
代码非常简单,算是观察者模式的“模板代码”,只能反映大体思路。观察者实现方式多种多样,在代码中遇到 register、attach、addObserver 等都可能是观察者模式的不同实现,不过万变不离其宗,设计思路是相似的。
观察者模式的应用场景
观察者模式应用非常广泛,比如发布-订阅系统,MQTT 协议是典型代表,股票价格监控,社交媒体的通知机制等。在移动开发中,有很多知名的三方库也应用了观察者模式,比如 EventBus、RxJava、ARouter、Lifecycle 等,它们都符合观察者模式的思想,只是实现方式会有不同,下面以 EventBus 为例,看它是如何实现的。
EventBus 是一种事件发布-订阅总线框架,旨在简化应用中组件间通信的复杂度。它的使用方式如下:
override fun onCreate(savedInstanceState: Bundle?) {
//1.注册观察者
EventBus.getDefault().register(this);
}
//2.定义观察方法,可指定执行线程
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEvent1(Event1 event) {
//...
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEvent2(Event2 event) {
//...
}
override fun onStop() {
//3.释放观察者
EventBus.getDefault().unregister(this);
}
//other:
//4.状态更新事件,此时内部会通知onEvent1执行
EventBus.getDefault().post(new Event1());
EventBus 的使用非常简单,不过需要及时解注册,否则会有内存泄漏的风险。下面跟随使用方式,具体来看 EventBus 是如何实现的:
register
:注册观察者,接收任何类型,不同于经典实现,只接收一种类型的观察者。@Subscribe
注解:定义接收的消息(Event)类型,以及观察者的通知方法。unregister
:移除观察者,会将其关联的 Event 方法移除。post
:为观察者发送消息,只通知可匹配的观察者,也就是接收的消息类型(@Subscribe 方法)等于发送消息类型(post 的 Event 类型),或是其父类。
EventBus 核心维护了一个映射表,在 register 时遍历注册对象中 Subscribe 注解的方法,并将方法消息类型和对应方法存入映射表,在 post 时查询映射表,找到对应消息类型匹配的方法,并通过反射调用的方式进行通知,这就是 EventBus 的设计思想。
在 EventBus 内部,还维护了线程池,让 Subscribe 注解可以设置对应参数,来切换同步阻塞方式或异步非阻塞方式的场景。也就是说,观察者模式既可以同步阻塞方式实现,也可以异步非阻塞方式,甚至可以跨进程注册和通知,这些都是观察者模式在不同场景的不同实现。
类似观察者模式思想的还有个生产者 - 消费者”模型概念,它们的区别和联系如下:
- 它们设计目的都是为了解耦。
- 生产-消费用缓存队列进行解耦,生产和消费是无感知的;观察者模式需要存储被观察者对象,是有感知的。
- 生产-消费是天然异步非阻塞的,观察者模式可以选择不同实现方式。
- 生产-消费是多对多的,观察者模式是一对多的。
总结
观察者模式的应用场景非常广泛,小到代码层面的解耦,大到架构层面的系统解耦,再或者一些产品的设计思路,都有这种模式的影子。再复习一下它的定义:观察者模式能够在对象之间建立灵活的依赖关系,并在状态变化时实现自动通知和更新。