观察者模式

Event-Subscriber、Listener、Observer

观察者模式也成作为发布订阅模式(Publish-Subscribe)

是一种订阅模式,能够在观察者和被观察者分离的情况下,将被观察者的事件通知给观察者;

在 Android 中也经常能看到和用到这种设计模式:

  • Application 中的registerActivityLifecycleCallbacks 允许对 Activity 生命周期变化事件的观察;

  • 对 View 设置 setOnClickListener 是对点击事件的观察;

  • LiveData、StateFlow、ShareFlow 这些容器允许订阅者观察到容器数据的变化;

  • ....

优点:

  • 遵守了 开闭原则,无需修改被观察者(发布者)代码就能引入新的观察者(订阅者)

  • 允许观察者动态地订阅或取消接收请求

例子

对于观察者和被观察者,之间不能存在耦合;

举个例子,假设有一个花店,存在多个客户,当新进货鲜花的时候需要通知到对应的客户:

  • 此时花店的商品是被观察者,客户是观察者,花店持有客户的引用,将客户和花店耦合在一起;

  • 当以后还需要新增加新的角色时,比如管理员 Admin、经理 Manager、员工等,都需要对 FlowerShop 这个类进行修改;

    这就违背了设计原则中的 开闭原则

 data class FlowerGoods(
     val name: String,
     val price: String
 )
 ​
 class Customer {
 ​
     fun onSubscribe(goods: FlowerGoods) {
         println("Now has new goods: $goods")
     }
 }
 ​
 ​
 class FlowerShop(
     private val customer: Customer
 ) {
     private val allFlowerGoods = mutableListOf<FlowerGoods>()
     // 进货新的鲜花
     fun addNewFlowers(goods: FlowerGoods) {
         allFlowerGoods.add(goods)
         customer.onSubscribe(goods)
     }
 }
 ​
 ​
 ​
 fun main() {
     val shop = FlowerShop(Customer())
     shop.addNewFlowers(
         FlowerGoods("向日葵", "10.00元")
     )
 }

使用观察者模式改善这种耦合关系:

  • 抽离出一个接口,用于通知各种事件的发生;同时让花店无需知道对方具体是什么角色,这就将观察者和被观察者分离;

     interface FlowGoodsChangeObserver {
         // 新增加时
         fun onAdd(goods: FlowerGoods)
         
         // 移除商品时
         fun onRemove(goods: FlowerGoods)
         
         // 有折扣时
         fun onDiscount(goods: FlowerGoods)
     }
  • 花店使用一个列表存储这些观察者,提供方法注册观察者

     class FlowerShop {
         private val allFlowerGoods = mutableListOf<FlowerGoods>()
         private val observers = mutableListOf<FlowerGoodsChangeObserver>()
         
         // 进货新的鲜花
         fun addNewFlowers(goods: FlowerGoods) {
             allFlowerGoods.add(goods)
             observers.forEach {
                 it.onAdd(goods)
             }
         }
         
         fun registerFlowerGoodsObserver(observer: FlowerGoodsChangeObserver) {
             this.observers.add(observer)
         }
     ​
         fun unregisterFlowerGoodsObserver(observer: FlowerGoodsChangeObserver) {
             this.observers.remove(observer)
         }
     }
  • 最后对于每一个不同的角色,只需要实现 FlowerGoodsChangeObserver 接口即可;

    当新增角色时,就无需直接修改被观察者,而是直接实现对应的接口,这就遵守了 开闭原则

实现

对于观察者,可以实现一个接口 XxxObserver 来抽离出通用的方法:

 interface XxxObserver {
     fun onXxxAdd(...) 
     
     fun onXxxChange(...)
 }

对于被观察者,也能实现一个接口 XxxObserable 来抽离出实现观察者模式的方法:

 interface XxxObserable {
     fun registerXxxObserable(observer: XxxObserver)
     
     fun unregisterXxxObserable(observer: XxxObserver)
 }

如果被观察着中存在多个事件需要被观察,那么可以将这些事件统一封装为一个 XxxEvent 类,交由观察者接口的一个方法进行处理:

  • Android 中的触摸事件 MotionEvent 便是如此(不是指观察者模式,而是指将多种类型的事件封装为同一个类)

 interface XxxObserver {
     fun onEvent(event: XxxEvent)
 }

从上面例子的观察者模式中,被观察者持有观察者的是一个列表:

 observer.forEach {
     it.onAdd(goods) 
 }

这是一个同步操作,只有上一个观察者处理完之后才能通知下一个观察者,如果当某一个观察者处理时间较久,就会导致后面的观察者收到事件通知比较晚;

此时可以考虑异步去通知;