四、观察者模式

观察者模式 :定义了一系列对象之间的一对多关系;当一个对象改变状态,其他依赖者都会收到通知并自动更新。

气象站监测应用的例子,此系统中的三个部分是:

  1. 气象站:获取实际气象数据的物理装置;(该对象与观察者模式关系不大)。
  2. WeatherData对象:追踪来气象站的数据,并更新布告板;属性有温度、湿度、气压。
  3. 布告板:显示目前天气状况给用户看。有不同的布告板:1、展示天气实时情况,2、气象统计,3、天气预报等等不同布告板。

直接编程实现方式

关系图

在这里插入图片描述

WeatherData代码

/**
 * @author gu
 * @date 2020/6/14 17:13
 * 气象数据
 */
public class WeatherData {

	// 其他代码

    private CurrentConditionalDisplay currentConditionalDisplay;
    private StatisticsDisplay statisticsDisplay;
    private  ForecastDisplay forecastDisplay;

    /**
     * 当气象测量更新时,该方法会被调用;
     * 方法里面调用了每个布告板的update方法
     *
     */
    public void measurementsChanged(){
        float temperature = getTemperature();
        float humidity = getHumidity();
        float pressure = getPressure();
        currentConditionalDisplay.update(temperature,humidity,pressure);
        statisticsDisplay.update(temperature,humidity,pressure);
        forecastDisplay.update(temperature,humidity,pressure);
    }

	// 其他代码
}

这种实现方式的缺点:

  1. 针对具体的实现编程,而不是针对接口;
  2. 对于新增一个布告板,就要修改WheatherData的代码实现;
  3. 无法动态的增加或删除布告板;
  4. 没有封装改变的部分;
  5. 破坏了weatherData类的封装。

改进

应该使用观察者模式实现该功能:
在这里插入图片描述
主题和观察者定义了一对多的关系;观察者依赖于此主题,只要主题状态一有变化,观察者就会被通知。根据通知的风格,观察者可能因此新值而更新。

使用观察者实现

为了交互对象之间的松耦合设计而努力。观察者模式提供了一种对象设计,让主题和观察者之间松耦合

设计类图

在这里插入图片描述

主题只知道观察者实现了某个接口;主题不需要知道观察者的具体类是谁、作了些什么或者其他任何细节。任何时候我们都可以新增或删除观察者。因为主题唯一依赖的东西是一个实现Observer接口的对象列表,所以可以随时的改变观察者的个数。

代码实现

WeatherData类:

/**
 * @author gu
 * @date 2020/6/14 17:13
 * 气象数据
 */
public class WeatherData implements Subject{
    private final ArrayList<Observer> observers;
    private float temperature;
    private float humidity;
    private float pressure;

    public WeatherData(ArrayList<Observer> observers) {
        this.observers = observers;
    }
    
	@Override
    public void registerObserver(Observer observer) {
        observers.add(observer);
    }

	@Override
    public void removeObserver(Observer observer) {
        int i = observers.indexOf(observer);
        if(i >= 0){
            observers.remove(i);
        }
    }
    
	@Override
    public void notifyObserver() {
        observers.forEach(o -> {
            o.update(getTemperature(),getHumidity(),getPressure());
        });
    }

    public void measurementsChanged(){
        notifyObserver();
    }

    public void setMeasurements(float temperature, float humidity, float pressure){
        this.temperature = temperature;
        this.humidity = temperature;
        this.pressure = pressure;
        measurementsChanged();
    }
    // 其他代码
}
 

CurrentConditionalDisplay实现


/**
 * @author gu
 * @date 2020/6/14 17:18
 * 展示当前气象的布告板
 */
public class CurrentConditionalDisplay implements Observer,DisplayElement{

    private float temperature;
    private float humidity;
    // 保留subject的引用,当想要主动注销时比较方便,在自身添加一个注销方法调用weatherData的注销方法即可
    private final Subject weatherData;

    public CurrentConditionalDisplay(Subject weatherData) {
        this.weatherData = weatherData;
        // 将自己注册到主题
        weatherData.registerObserver(this);
    }

    @Override
    public void update(float temperature, float humidity, float pressure){
        this.temperature = temperature;
        this.humidity = humidity;
        display();
    }

    /**
     * 只显示温度和湿度
     */
    @Override
    public void display() {
        System.out.println("CurrentConditionalDisplay: temperature = [" + temperature + "], humidity = [" + humidity + "]");
    }
}

ForecastDisplayStatisticsDisplay两个类的实现类似,只是display()方法不同:


ForecastDisplay:
@Override
public void display() {
    System.out.println("ForecastDisplay: temperature = [" + temperature + "], humidity = [" + humidity + "], pressure = [" + pressure + "]");
}

StatisticsDisplay:
@Override
public void display() {
    System.out.println("StatisticsDisplay: tody temperature = [" + temperature + "], humidity = [" + humidity + "], pressure = [" + pressure + "]");

}

以上主题将参数传给观察者使用的是主动将所有参数推送过去的;也可以在主题中添加相应的方法,update()方法不设置参数,让每个观察者去拉数据,这样观察者可以按需索取,但是主题会被调用多次。

jdk自带的观察者模式

主题:Observable

在这里插入图片描述

观察者Observer

在这里插入图片描述
可以看出,jdk的Observable类提供了两种数据交换的方法,主动传参和被动拉去;
在这里插入图片描述

在java的内置观察者模式中,主题Observable是以类的形式出现的;多了几个change相关的方法;
在这里插入图片描述

主题推送消息的步骤:

  1. 先调用setChange()方法,标记状态已经改变的事实;
  2. 然后调用两个notifyObserver()方法中的其中一个。

setChanged()方法用来标记状态十分方式了改变,好让notifyObservers()知道当它被调用时应该更新观察者;如果调用notifyObservers()之前没有先调用setChanged()方法,观察者就不会被通知。
这样做的必要性:setChanged()方法可以在更新观察者时有更多的弹性,选择更适当地时候通知观察者。比如:当温度变化超过一度时才通知观察者,而不是温度变化十分之一度就告知观察者。
同时还有clearChange()方法,和hasChanged()方法。

观察者接收通知:

// o 主题本身作为第一个参数传给观察者,告知观察者是哪个主题通知它
// arg 主题传给观察者的参数,为null说明为空
void update(Observable o, Object arg);

jdk的实现方式有些缺点:Observable不是一个接口,而是一个类。jdk中swing中大多数都是用了监听者模式来实现。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值