设计模式之观察者模式(二)

时间:2024-01-24 07:57:57

设计模式之观察者模式(二)

上一篇的观察者模式学习的还好吗?首先简单来回顾下上篇内容,有一个气象站的需求,需要在温度、湿度、气压改变的时候,实时更新三个布告板,以便能及时、准确的获取信息。所以,在设计模式的层面,我们最容易想到并且最正确的方式就是使用观察者模式来处理这个问题。

上一篇,我们通过一系列的分析,并画出符合要求的类图,然后使用了第一种方式,通过自己动手写设计模式实现了观察者模式的功能。但是,截止到目前为止,我们观察者模式的方式都是由被观察者主动发送状态通知给观察者,从而达到实时更新。这样有个弊端就是被观察者不能事先知道观察者每个人的需求,有些观察者其实只需要一点点数据,但无奈会受到一堆数据。有没有方式,让观察者通过getter方法,主动获取新增的状态呢。答案是,有。Java内置的Observer模式两种方法都支持。

java.util包内包含基本的Observer接口与Observable类,这和我们的Subject接口与Observer接口很相似。Observer接口与Observable类使用上更方便。因为许多功能都事先准备好了。你甚至可以使用推(PUSH)或拉(PULL)的方式传送数据。下面就看看,我们修改后的气象站OO设计。
image

Java内置的观察者模式如何运作

Java内置的观察者模式运作方式,和我们在气象站中的实现类似,但有一点小差异。最明显的差异是WeatherData现在扩展自Obserable类,并继承到一些增加、删除、通知观察者的方法(以及其他的方法)。Java版本的用法如下:

如何把对象变成观察者

如同以前一样,实现观察者接口(java.util.Observer),然后调用任何Observable对象的 addObserver()方法。不想再当观察者时,调用deleteObserver()方法即可。

可观察者要如何送出通知

首先,你需要利用扩展java.util.Observable接口产生"可观察者"类 ,然后,需要两个步骤:

  1. 先调用setChanged()方法,标记状态已经改变的事实
  2. 然后调用两种notifyObservers()方法中的一个:notifyObservers()或notifyObservers(Object arg)

观察者如何接收通知

同以前一样,观察者实现了更新的方法,但是方法的签名不太一样:
update(Observable o, Object arg)
o:主题本身当做第一个变量,好让观察者知道是哪个主题通知它的
arg:这正是传入notifyObservers()的数据对象。如果没有说明,则为空。
如果你想推(push)数据给观察者,你可以把数据当做数据对象传给notifyObservers(arg0)方法。否则,观察者就必须从可观察者对象中拉(pull)数据,如何拉数据,不急不急,我们很快和大家见面。

利用内置的支持重做气象站

首先,把WeatherData改成使用java.util.Observable

package com.dimple.headfirst.observer;

// 1:记得要导入(import)正确的Observer/Observable
import java.util.Observable;

/**
 * 
 * @Title:
 * @Description: 2:我们现在正继承Observable
 * 3:我们不需要追踪观察者了,也不需要管理注册与删除(让超类代劳即可)。
 * 我们把注册、添加、通知的相关代码删除
 */
public class WeatherDataNew extends Observable {
    private float temperature;
    private float humidity;
    private float pressure;
    
    // 4:我们的构造器不再需要为了记住观察者们而建立数据结构了
    public WeatherDataNew() {
        
    }
    
    public void measurementsChanged() {
        // 5: 调用notifyObservers()之前,要先调用setChanged()来指示状态已经改变
        setChanged();
        // 注意:我们没有调用notifyObservers()传送数据对象,这表示我们采用的做法是拉
        notifyObservers();
    }
    
    public void setMeasurements(float temperature, float humidity,float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        measurementsChanged();
    }
    
    // 6:这些并不是新方法,只是因为我们要使用“拉”的做法,所以才提醒你有这些方法。观察者会利用这些方法取得WeatherData对象的状态
    public float getTemperature() {
        return temperature;
    }
    
    public float getHumidity() {
        return humidity;
    }
    
    public float getPressure() {
        return pressure;
    }
}

现在,让我们重做CurrentConditionsDisplay

package com.dimple.headfirst.observer;

import java.util.Observable;
// 1:导入正确的Observer/Observable
import java.util.Observer;

/**
 * 
 * @Title:
 * @Description: 2:我们正在实现java.util.Observer接口
 */
public class CurrentConditionDisplayNew implements Observer, DisplayElement {
    
    Observable observable;
    private float temperature;
    private float humidity;
    
    // 3:构造器需要 Observable当参数,并将 CurrentConditionsDisplay对象登记成观察者
    public CurrentConditionDisplayNew(Observable observable) {
        this.observable = observable;
        observable.addObserver(this);
    }

    // 4:改变update方法,增加Observable和数据对象作为参数
    @Override
    public void update(Observable obs, Object arg) {
        // 5:在update()中,先确定可观察者属于WeatherData类型,然后利用getter方法获取温度和湿度测量值,最后调用display() 
        if (obs instanceof WeatherDataNew) {
            WeatherDataNew weatherDataNew = (WeatherDataNew)obs;
            this.temperature = weatherDataNew.getTemperature();
            this.humidity = weatherDataNew.getHumidity();
            display();
        }
    }

    // 显示温度和湿度
    @Override
    public void display() {
        System.out.println("Current conditions: " + temperature + " F degrees and " + humidity + "% humidity");
    }
}

运行新的代码

public class WeatherStation {
    public static void main(String[] args) {
        WeatherDataNew weatherDataNew = new WeatherDataNew();
        CurrentConditionDisplayNew currentConditionDisplayNew = new CurrentConditionDisplayNew(weatherDataNew);
        weatherDataNew.setMeasurements(80, 65, 30.4f);
    }
}

// 结果显示
Current conditions: 80.0 F degrees and 65.0% humidity

至此,我们就把Java内置的观察者模式学习了一遍,你学会了吗?

但是,不知道你发现没有,内置的观察者模式中,可观察者是一个“类”而不是一个“接口”,甚至没有实现一个接口,这样就限制了他的使用和复用,所以还是需要做下强调,提醒大家这是一个“类”的事实。

Observable是一个类

首先,因为Observable是一个“类”,你必须设计一个类继承它。如果某类想同事具有Observable类和另一个超类的行为,就会有纠结,毕竟Java只支持单继承嘛。

Observable将关键的方法保护起来

 protected synchronized void setChanged() {
        changed = true;
    }

setChanged()方法是一个保护方法,这就意味着除非你继承自Observable,否则你无法创建Observable实例并组合到你自己的对象中来。这个设计也就违反了之前第二个设计原则“多用组合,少用继承”。

做什么呢?

如果你能够扩展java.util.Obserable,那么Observable“可能”可以符合你的需求。否则,你可能需要像我们上一篇那样,自己去实现一套观察者模式,不过不管怎样,相信你都已经掌握了观察者模式,这已经难不倒你了。

因为观察者模式是平时使用比较广泛,并且大家会频繁的接触甚至是使用,所以我本来想在这篇进行总结的,考虑到篇幅的原因,让这个总结放到下一篇,通过这两个篇幅的学习和实践,把自己动手写观察者模式以及使用Java内置的方式来使用观察者模式都进行了掌握。其他语言的不知道有没有内置的,只能说Java程序员在看完这篇文章之后,肯定又多了一种选择,给自己鼓掌吧。

PS:代码已经上传,需要查看的朋友点击此处HeadFirstDesign

爱生活,爱学习,爱感悟,爱挨踢

image

posted on 2019-03-29 17:30 小酒窝 阅读(...) 评论(...) 编辑 收藏