观察者设计模式

观察者设计模式

在此模式中,有两个角色,一个是主题,一个是观察者。

概念:

观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖着都会收到通知并自动更新。

场景

假设现在有一个气象监测应用,其中有三个部分:
1、气象站:通过各种感应装置获取到气象数据
2、WeatherData对象:取得数据
3、布告板:显示数据

我们要做的,就是利用WeatherData取得数据,并更新每个布告板,这种情况就很适合观察者模式。

思路:

WeatherData对象是一个主题,它的功能就是向每个布告板(也即观察者)发送它最新获取到的数据。
所以,它内部应该有个List列表,泛型是观察者接口(面向接口编程),每次它获取到数据,就去遍历这个List,然后调用观察者的更新方法。

类图

在这里插入图片描述

代码实现:

接口

主题接口

public interface Subject {
    public void registerObserver(Observer observer);
    public void removeObserver(Observer observer);
    public void notifyObservers();
}

观察者接口

public interface Observer {
    public void update(float temp,float humidity,float pressure);
}

展示接口
这个接口跟观察者模式没关系,只是这里的场景是个布告板,需要对数据展示,所以弄了这么一个接口

public interface DisplayElement {
    public void display();
}
实现类

WeatherData

public class WeatherData implements Subject {

    private java.util.List<Observer> observers;
    private float temperature;
    private float humidity;
    private float pressure;

    public WeatherData(){
        observers=new ArrayList<>();
    }


    @Override
    public void registerObserver(Observer observer) {
            observers.add(observer);
    }

    @Override
    public void removeObserver(Observer observer) {
            observers.remove(observer);
    }

    @Override
    public void notifyObservers() {
            for (Observer o:observers){
                o.update(temperature,humidity,pressure);
            }
    }

    public void measurementsChanged(){
        notifyObservers();
    }

    public void setMeasurements(float temperature,float humidity,float pressure){
        this.temperature=temperature;
        this.humidity=humidity;
        this.pressure=pressure;
        measurementsChanged();
    }
}

布告板

public class CurrentConditionsDisplay implements Observer, DisplayElement {

    private float temperature;
    private float humidity;

	//这个方法是我自己添加的,与书中不同,但我认为应该有这么一个订阅的方法
    public void subscribe(Subject subject){
        subject.registerObserver(this);
    }

	//以及这个取消订阅的方法
    public void cancelSubscribe(Subject subject){
        subject.removeObserver(this);
    }

    @Override
    public void display() {
        System.out.println("Current conditions: "+temperature+"F degrees and "+ humidity+"% humidity");
    }

    @Override
    public void update(float temp, float humidity, float pressure) {
        this.temperature=temp;
        this.humidity=humidity;
        display();
    }
}
测试
    public static void main(String[] args) {
    	//主题
        WeatherData weatherData=new WeatherData();
        //观察者
        CurrentConditionsDisplay currentConditionsDisplay=new CurrentConditionsDisplay();
        //观察者去订阅气象数据(其实就是让主题的list加上当前观察者)
        currentConditionsDisplay.subscribe(weatherData);
        //当它拿到数据,就遍历list中的每个观察者,分别调用这些观察者的update方法
        weatherData.setMeasurements(1.0f,2.0f,3.0f);
    }

结果:
在这里插入图片描述

Java内置的观察者模式

java.util包内置了 Observer接口 和 Observable

源码

Observer接口就观察者
public interface Observer {
    void update(Observable o, Object arg);
}
Observable类就是主题
public class Observable {
    private boolean changed = false;
    private Vector<Observer> obs;

    public Observable() {
        obs = new Vector<>();
    }
	
	//订阅
    public synchronized void addObserver(Observer o) {
        if (o == null)
            throw new NullPointerException();
        if (!obs.contains(o)) {
            obs.addElement(o);
        }
    }	
    //取消订阅
    public synchronized void deleteObserver(Observer o) {
        obs.removeElement(o);
    }

    public void notifyObservers() {
        notifyObservers(null);
    }

  
    public void notifyObservers(Object arg) {
        Object[] arrLocal;

        synchronized (this) {
        	//若没有改变,无需通知观察者
            if (!changed)
                return;
            //这里为啥非得转成Object[]数组呢?直接遍历Vector不行吗?---大佬们请在评论区解惑
            arrLocal = obs.toArray();
            //把changed标识设置为false
            clearChanged();
        }
		
		//逐个通知观察者
        for (int i = arrLocal.length-1; i>=0; i--)
            ((Observer)arrLocal[i]).update(this, arg);
    }
    public synchronized void deleteObservers() {
        obs.removeAllElements();
    }
    protected synchronized void setChanged() {
        changed = true;
    }
    protected synchronized void clearChanged() {
        changed = false;
    }

    public synchronized boolean hasChanged() {
        return changed;
    }
    
    public synchronized int countObservers() {
        return obs.size();
    }
}

上面的代码中,这个changed是干嘛用的呢?
书中解释如此:
比如气象站测量比较敏锐,温度计读数每十分之一度就会更新,这会造成WeatherData对象不断地通知观察者,我们可能觉得这样太频繁了,只希望半度以上才更新,那么就可以在温度差距达到半度时,调用setChanged()方法,达到有效的更新。

那我们现在要做的,是用Java里面写好的观察者模式,去重写改造上面的气象站

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

改造后代码如下

public class CurrentConditionsDisplay implements Observer, DisplayElement {

    private float temperature;
    private float humidity;

    public void subscribe(Observable subject){
        subject.addObserver(this);
    }

    public void cancelSubscribe(Observable subject){
        subject.deleteObserver(this);
    }

    @Override
    public void display() {
        System.out.println("Current conditions: "+temperature+"F degrees and "+ humidity+"% humidity");
    }

    @Override
    public void update(Observable o, Object arg) {
        if (o instanceof WeatherData){
            WeatherData weatherData=(WeatherData)o;
            this.temperature=weatherData.getTemperature();
            this.humidity=weatherData.getHumidity();
            display();
        }
    }
}
public class WeatherData  extends Observable {

    private float temperature;
    private float humidity;
    private float pressure;


    public void measurementsChanged(){
        //要先调用setChanged表示状态已改变
        setChanged();
        //这里实际上调用notifyObservers(null),没有传递数据,所以是观察者来拉取我们的数据
        notifyObservers();
    }

    public void setMeasurements(float temperature,float humidity,float pressure){
        this.temperature=temperature;
        this.humidity=humidity;
        this.pressure=pressure;
        measurementsChanged();
    }

    public float getTemperature() {
        return temperature;
    }

    public float getHumidity() {
        return humidity;
    }

    public float getPressure() {
        return pressure;
    }
}

另外,在Swing中许多地方也用到了观察者模式,比如JButton

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值