认识观察者模式
先来看一下报纸和杂志的订阅是怎么回事:
1,报纸的业务是出版报纸
2,向某家报社订阅报纸,只要他们有新报纸出版,就会给你送过来。只要你是他们的订户,你就一直会收到报纸
3,当不在想看报纸的时候,取消订阅,他们就不会给你送新的报纸了。
4,只要报社还在运营,就会一直有人去想他们订阅或者取消订阅报纸
如果能理解上面的,那么你已经知道观察者是怎么回事了
只是名字不一样而已,出版社改名为(主题)Subject,订阅者改名为(观察者)Observer
观察者模式定义
定义了对象之间一对多的依赖,这样一来,当一个对象发生改变时,其他的所有依赖着都会收到通知并且自动更新
主题和观察者定义了一对多的关系,只要主题有变化,观察者就会被通知。
案例1
有一个气象站,用来检测气象,还有不知道多少个公告板,用来展示天气情况。
每当天气发送变化时,公告板都要做出及时的更新
实现如下:
1,首先要定义主题接口,内部要能添加公告板,删除公告板和更新公告板
2,观察者接口,所有的需要有一个更新数据的方法,并且还有一个现实数据的方法(不是必须的)
/** * 主题 */ interface Subject { /** * 注册观察者 */ fun registerObserver(observer: Observer) /** * 删除观察者 */ fun removeObserver(observer: Observer) /** * 主题发生改变是,用来通知所有的观察者 */ fun notifyObservers() } /** * 观察者接口,所有的观察者必须实现此接口 */ interface Observer { /** * 更新数据 */ fun update(temp: Float, humidity: Float, pressure: Float) } interface DisplayElement { /** * 显示数据 */ fun display() }
3,实现唯一的气象站(主题)
class WeatherData : Subject { private var observers: MutableList<Observer> = mutableListOf() private var temperature: Float = 0F private var humidity: Float = 0F private var pressure: Float = 0F //添加 override fun registerObserver(observer: Observer) { observers.add(observer) } //删除 override fun removeObserver(observer: Observer) { observers.remove(observer) } //更新公告板 override fun notifyObservers() { observers.forEach { it.update(temperature, humidity, pressure) } } //通知更新 fun measurementsChanged() { notifyObservers() } //获取数据 fun setMeasurements(temp: Float, humidity: Float, pressure: Float) { temperature = temp this.humidity = humidity this.pressure = pressure measurementsChanged() } }
4,实现所有的公告板
class CurrentConditionsDisplay(weatherData: Subject) : Observer, DisplayElement { var temperature: Float = 0F var humidity: Float = 0F private var weatherData: Subject = weatherData init { this.weatherData.registerObserver(this) } override fun update(temp: Float, humidity: Float, pressure: Float) { temperature = temp this.humidity = humidity display() } override fun display() { println( """ 公告一: 温度:$temperature 湿度:$humidity """.trimIndent() ) } }
class ForecastDisplay(private var weatherData: Subject) : Observer, DisplayElement { var temperature: Float = 0F var pressure: Float = 0F init { this.weatherData.registerObserver(this) } override fun update(temp: Float, humidity: Float, pressure: Float) { temperature = temp this.pressure = pressure display() } override fun display() { println( """ 公告二: 温度:$temperature 压力:$pressure """.trimIndent() ) } }
class StatisticsDisplay(private var weatherData: Subject) : Observer, DisplayElement { var temperature: Float = 0F var humidity: Float = 0F var pressure: Float = 0F init { this.weatherData.registerObserver(this) } override fun update(temp: Float, humidity: Float, pressure: Float) { temperature = temp this.humidity = humidity this.pressure = pressure display() } override fun display() { println( """ 公告三: 温度:$temperature 湿度:$humidity 压力:$pressure """.trimIndent() ) } }
5,测试:
fun main() { val weatherData = WeatherData() //创建三个公告板的对象 CurrentConditionsDisplay(weatherData) ForecastDisplay(weatherData) StatisticsDisplay(weatherData) weatherData.setMeasurements(23f, 4.43f, 34f) }
6,结果:
公告一: 温度:23.0 湿度:4.43 公告二: 温度:23.0 压力:34.0 公告三: 温度:23.0 湿度:4.43 压力:34.0
问题
观察者种类绝非者三种,主题不可能事先知道每个人的需求,所以还是让观察者自己去拿到需要的数据,这样一来就不会强迫收到一堆数据。这么做在以后也比较容易修改。如果有一天要扩展功能,你不需要修改对每位观察者的调用,只需要在主题中添加对应的数据供观察者获取即可。
解决
使用 JAVA 内置的观察者模式,首先看一下它提供的类:
ObServable:相当于主题,我们的气象站需要实现它
ObServer:观察者,我们的所有公告板都要实现它
案例1的升级
这一次我们不用定义主题了,因为 JAVA 已经内置了。我们只需要继承自 JAVA 提供的主题类即可,如下:
1,创建气象站
class WeatherData : Observable() { var temperature: Float = 0F var humidity: Float = 0F var pressure: Float = 0F private fun measurementsChanged() { //在 notify 之前,必须调用 setChanged 表示状态发生改变 setChanged() notifyObservers() } fun setMeasurements(temp: Float, hum: Float, press: Float) { temperature = temp humidity = hum pressure = press measurementsChanged() } }
注意:刷新之前必须调用 setChanged 方法通知状态发生改变,有疑惑的可以看一下源码,非常简单
2,创建观察者,实现默认的观察者接口
class CurrentConditionsDisplay(private var weatherData: Observable) : Observer, DisplayElement { private var temperature: Float = 0F private var humidity: Float = 0F init { //订阅观察者 this.weatherData.addObserver(this) } //更新 override fun update(o: Observable?, arg: Any?) { //只能转换为主题对象 if (o is WeatherData) { this.temperature = o.temperature this.humidity = o.humidity } display() } //显示 override fun display() { println( """ 公告一: 温度:$temperature 湿度:$humidity """.trimIndent() ) } }
class ForecastDisplay(private var weatherData: Observable) : Observer, DisplayElement { private var temperature: Float = 0F private var pressure: Float = 0F init { this.weatherData.addObserver(this) } override fun update(o: Observable?, arg: Any?) { if (o is WeatherData) { this.temperature = o.temperature this.pressure = o.pressure } display() } override fun display() { println( """ 公告二: 温度:$temperature 压力:$pressure """.trimIndent() ) } }
class StatisticsDisplay(private var weatherData: Observable) : Observer, DisplayElement { var temperature: Float = 0F var humidity: Float = 0F var pressure: Float = 0F init { this.weatherData.addObserver(this) } override fun update(o: Observable?, arg: Any?) { if (o is WeatherData) { this.temperature = o.temperature this.humidity = o.humidity this.pressure = o.pressure } display() } override fun display() { println( """ 公告三: 温度:$temperature 湿度:$humidity 压力:$pressure """.trimIndent() ) } }
3,测试
fun main() { val weatherData = WeatherData() CurrentConditionsDisplay(weatherData) ForecastDisplay(weatherData) StatisticsDisplay(weatherData) weatherData.setMeasurements(23123f, 4.43f, 34f) }
总的来说改动不大,原来返回的是所有的数据,现在是主题,观察者可以直接获取到自己需要的内容。
但是效果却非常大,特别是在需要扩展的时候,不需要修改别的观察者了。而且 JAVA 内置的观察者做了很多处理。
JAVA 内置的观察者的缺陷
通过查看源码可以看到,Observable 是一个类,并非接口且没有实现一个接口。所以他的限制比较大。
因为 Observable 是一个类,我们必须继承他,如果要继承别的类,就会陷入两难,毕竟 Java 不支持多继承
ObServable 没有实现接口,所以无法建立自己的实现。而且 setChanged 被保护起来了。除非你继承自 ObServable。
其实你完全可以写出一个适用于你自己的观察者模式,反正你已经非常熟悉观察者模式了,不是吗?