观察者模式

认识观察者模式

先来看一下报纸和杂志的订阅是怎么回事:

​ 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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Tʀᴜsᴛ³⁴⁵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值