认识观察者模式
先来看一下报纸和杂志的订阅是怎么回事:
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。
其实你完全可以写出一个适用于你自己的观察者模式,反正你已经非常熟悉观察者模式了,不是吗?
参考自:Head First