观察者模式

简介: 观察者模式

认识观察者模式


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


1,报纸的业务是出版报纸


2,向某家报社订阅报纸,只要他们有新报纸出版,就会给你送过来。只要你是他们的订户,你就一直会收到报纸


3,当不在想看报纸的时候,取消订阅,他们就不会给你送新的报纸了。


4,只要报社还在运营,就会一直有人去想他们订阅或者取消订阅报纸


如果能理解上面的,那么你已经知道观察者是怎么回事了


只是名字不一样而已,出版社改名为(主题)Subject,订阅者改名为(观察者)Observer


观察者模式定义


定义了对象之间一对多的依赖,这样一来,当一个对象发生改变时,其他的所有依赖着都会收到通知并且自动更新


20190212181743594.jpg


主题和观察者定义了一对多的关系,只要主题有变化,观察者就会被通知。


案例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。


其实你完全可以写出一个适用于你自己的观察者模式,反正你已经非常熟悉观察者模式了,不是吗?


相关文章
|
3月前
|
设计模式 监控 C#
观察者模式
观察者模式是一种行为型设计模式,用于定义对象间的一对多依赖关系,使得当一个对象的状态发生变化时,所有依赖于它的对象都会自动收到通知并更新。该模式主要用于实现发布-订阅机制。核心角色包括主题(Subject)、观察者(Observer)、具体主题(Concrete Subject)和具体观察者(Concrete Observer)。优点包括低耦合、动态添加观察者和自动更新,但也有可能引起过多更新、不适合同步通知和可能造成内存泄漏等缺点。适用于气象站数据更新、股票价格监控和用户界面组件更新等场景。
65 4
|
10月前
|
C++
【C++】—— 观察者模式
【C++】—— 观察者模式
|
关系型数据库 API
观察者模式解读
观察者模式解读
|
10月前
|
设计模式 JavaScript 开发者
详细讲解什么是观察者模式
详细讲解什么是观察者模式
|
10月前
|
设计模式 Java
【观察者模式】 ——每天一点小知识
【观察者模式】 ——每天一点小知识
102 0
5 # 观察者模式
5 # 观察者模式
47 0
|
10月前
|
设计模式 Java
【设计模式系列笔记】状态模式
在Java中,状态模式是一种行为设计模式,它允许对象在其内部状态改变时改变其行为。状态模式的关键思想是将对象的状态封装成独立的类,并将对象的行为委托给当前状态的对象。这样,当对象的状态发生变化时,其行为也会相应地发生变化。
117 0
|
XML 设计模式 Java
观察者模式(下)
观察者模式(下)
88 0
|
设计模式
观察者模式(上)
观察者模式(上)
120 0
|
设计模式
我学会了,观察者模式
观察者模式属于行为型模式,这个类型的设计模式总结出了 类、对象之间的经典交互方式,将类、对象的行为和使用解耦了,花式的去使用对象的行为来完成特定场景下的功能。
153 0
我学会了,观察者模式