观察者模式

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

认识观察者模式:

可以通过看报纸和杂志订阅来认识观察者模式:

  1. 报社的业务就是出版报纸。
  2. 向某家报社订阅报纸,只要他们有新报纸出版,就会给你送来。只要你是他们的订户,你就会一直收到新报纸。
  3. 当你不想再看报纸的时候,取消订阅,他们就不会再送新报纸来。
  4. 只要报社还在运营,就会一直有人(或单位)向他们订阅报纸或取消订阅报纸。

在这里插入图片描述

出版者 + 订阅者 = 观察者模式

如果你了解报纸的订阅是怎么回事,其实就知道观察者模式是怎么回事,只是名称不太一样:出版者改称为“主题”(Subject),订阅者改称为“观察者”(Observer)。

即:主题(Subject) + 观察者(Observer) = 观察者模式

定义观察者模式:类图

在这里插入图片描述

通过一个例子实现观察者模式:当天气发生变化时,通知公告板(可能有很多块)改变天气内容。

分析例子可以知道,此时公告板相当于观察者,天气相当于主题。

主题(Subject) + 观察者(Observer) = 观察者模式

一:自定义观察者模式

创建主题,和观察者,及公告板的显示接口

interface Subject {
	//注册观察者
    void registerObserver(Observer observer);
	//移除(取消注册)观察者
    void removeObserver(Observer observer);
	//更新观察者
    void notifyObserver();
}
interface Observer {

    void update(WeatherData weatherData);

}
public interface DisplayElement {

    //显示公告
    void display();

}
WeatherData实现Subject,成为主题的实例对象
class WeatherData implements Subject {

    private List<Observer> mObservers;
    private float t1,t2,t3;//温度,湿度,气压

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

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

    @Override
    public void removeObserver(Observer observer) {
        int i = mObservers.indexOf(observer);
        if (i>=0){
            mObservers.remove(i);
        }
    }

    @Override
    public void notifyObserver() {
        for (Observer observer: mObservers){
            observer.update(this);
        }
    }

    /**
     * 气象站更新天气变化时,通知观察者们
     */
    public void measurementsChanged(){
        notifyObserver();
    }

    public void setMeasurements(float t1, float t2, float t3){
        this.t1 = t1;
        this.t2 = t2;
        this.t3 = t3;
        measurementsChanged();
    }

    public float getT1() {
        return t1;
    }

    public float getT2() {
        return t2;
    }

    public float getT3() {
        return t3;
    }
}

创建显示天气的公告板D1,D2,D3.实现Observer接口,实现其update方法更新内容

class D1 implements Observer, DisplayElement {

    private float t1, t2;
    private Subject mWeathData;//方便以后取消订阅

    public D1(Subject weathData) {
        this.mWeathData = weathData;
        mWeathData.registerObserver(this);
    }

    @Override
    public void update(WeatherData weatherData) {
        this.t1 = weatherData.getT1();
        this.t2 = weatherData.getT2();
        display();
    }


    @Override
    public void display() {
        System.out.println("D1:->  温度:" + t1 + "   湿度:" + t2);
    }
}

执行程序:

 	public static void main(String[] args) {

        WeatherData weatherData = new WeatherData();

        D1 d1 = new D1(weatherData);
        D3 d3 = new D3(weatherData);
        D2 d2 = new D2(weatherData);

        //手动设置天气改变
        weatherData.setMeasurements(30, 16, 22);
        System.out.println("------------------------");
        //手动设置天气改变
        weatherData.setMeasurements(20, 18, 18);
        System.out.println("------------------------");
        //手动设置天气改变
        weatherData.setMeasurements(15, 10, 10);

    }

运行结果

D1:->  温度:30.0   湿度:16.0
D3:->  温度:30.0   湿度:16.0   气压:22.0
D2:->  温度:30.0   湿度:16.0   气压:22.0
------------------------
D1:->  温度:20.0   湿度:18.0
D3:->  温度:20.0   湿度:18.0   气压:18.0
D2:->  温度:20.0   湿度:18.0   气压:18.0
------------------------
D1:->  温度:15.0   湿度:10.0
D3:->  温度:15.0   湿度:10.0   气压:10.0
D2:->  温度:15.0   湿度:10.0   气压:10.0

上面就是一个完整的观察者模式案例了,当天气发生变化时会调用measurementsChanged()方法,measurementsChanged会调用notifyObserver通知观察者自己有数据更新。

二:使用Java内置的观察者模式

其实Java内置了观察者模式,我们也可以不必自己实现Subject和Observer。可以使用Java自带的Observable可观察的和Observer观察者。区别是Observable是一个类而Subject是一个接口。下面是Java中Observable和Observer源码:

public interface Observer {
    void update(Observable o, Object arg);
}
public class Observable {
    private boolean changed = false;
    private Vector<Observer> obs;

    /** Construct an Observable with zero Observers. */

    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) {
            /* 
             * 刷新之前要把change设置为false,否则通知发送不下去
             */
            if (!changed)
                return;
            arrLocal = obs.toArray();
            clearChanged();
        }

        for (int i = arrLocal.length-1; i>=0; i--)
            ((Observer)arrLocal[i]).update(this, arg);
    }

    public synchronized void deleteObservers() {
        obs.removeAllElements();
    }

    /**
     * 设置change状态
     */
    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();
    }
}

可以看出Observer没有改变,还是只有一个update()方法,通知观察者更新数据。
Observable中依然有注册和更新和解除注册的方法,唯一的区别就是添加了状态change的状态,可以根据Observable 中内容是否发生改变灵活运用或者不用。

此时WeatherData可以这样写

class WeatherData extends Observable {

    private float temperature;//温度
    private float humidity; //湿度
    private float pressure; //气压

    public WeatherData(){}

    //天气发生变化
    public void measurementsChanged(){
        setChanged();
        notifyObservers();
    }

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

    public float getHumidity() {
        return humidity;
    }

    public float getPressure() {
        return pressure;
    }

    public float getTemperature() {
        return temperature;
    }

公告板D1,D2,D3代码

class D1 implements Observer,DisplayElement {

    private Observable mObservable;//方便有需要的话取消订阅
    private float temperature;
    private float humidity;

    /**
     * 构造方法中订阅
     * @param observable
     */
    public D1(Observable observable){
        this.mObservable = observable;
        observable.addObserver(this::update);
    }

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

    @Override
    public void display() {
        System.out.println("D1:->  温度:" + temperature + "   湿度:" + humidity);
    }

运行代码:

  public static void main(String[] args) {

        WeatherData weatherData = new WeatherData();
        new D1(weatherData);
        new D2(weatherData);
        new D3(weatherData);

        //天气发生变化
        weatherData.setMeasurements(10, 10, 23);
        System.out.println("--------------------------------");
        //天气发生变化
        weatherData.setMeasurements(15, 15, 16);
        System.out.println("--------------------------------");
        //天气发生变化
        weatherData.setMeasurements(-1, 5, 6);
    }

运行结果:

D3:->  温度:10.0   湿度:10.0   气压:23.0
D2:->  温度:10.0   湿度:10.0   气压:23.0
D1:->  温度:10.0   湿度:10.0
--------------------------------
D3:->  温度:15.0   湿度:15.0   气压:16.0
D2:->  温度:15.0   湿度:15.0   气压:16.0
D1:->  温度:15.0   湿度:15.0
--------------------------------
D3:->  温度:-1.0   湿度:5.0   气压:6.0
D2:->  温度:-1.0   湿度:5.0   气压:6.0
D1:->  温度:-1.0   湿度:5.0

嗯!你注意到差别了吗?再看一次……,(反正我第一次没注意到)
两次代码的执行结果是一样的,只是显示的次数并没有按照第一次那样D1 -> D2 -> D3。这次是D3 -> D2 -> D1Java内置的观察模式通知的次序与我们自己实现的观察者次序是相反的,只要不依赖次序,其实也没什么影响。

有多个观察者时,不可以依赖特定的通知次序。

两种方式的区别:

以上就是观察者模式的两种实现方式,一种是实现Subject接口,一种是继承Observable类。两者之间的区别就是实现接口的Subject具备了接口的灵活的特性,而Observable需要继承具有一定的局限性。可以根据实际需求决定采用哪种方式

OO原则:
  • 封装变化
  • 多用组合,少用继承
  • 针对接口编程,不针对实现编程
  • 为交互对象之间的松耦合设计而努力

参考:《Head First设计模式》

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值