设计模式-观察者模式

229 阅读4分钟

观察者模式

0 定义

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

1 简述

观察者模式可以想象一个场景,就是以前订阅报纸的情况,首先需要去出版社去注册自己的信息,出版社根据注册信息表来配送报纸的地址,观察者模式的大致情况也是这样的,只不过名词换一下,出版社换成主题,订阅者换成观察者,观察者模式一般有两种实现方式,拉和推;如图一所示,观察者模式的UML图, image.png

2 实例

本身JAVA有实现观察者模式,我们这里就用JAVA里的来介绍实例,建议阅读一下java.util.Observable、java.util.Observer,Observable是一个主题,Observer是观察者。 我们先提一个需求,现在有一个天气公司可以获取各自数据如温度、湿度、风向等,我们需要根据其数据来实现不同类型的公告版,并且当数据更新的时候,需要通知公告板去获取数据。例如有一个公告板实现温度预测,有一个实现当前温度说明等等,展现出不同的形式。首先我们先忘记观察者模式,想象有其他我们学习的可以实现吗?比如策略模式,使用策略模式确实可以封装变化,当我们使用公告板的时候就可以加上其实现类就行,但是他无法做到通知公告板去更新数据。所以是不可以的,别问我其他的模式,我还没有学会。
主题实现

import java.util.Observable;

public class WeatherData extends Observable {
    private float temperature;
    private float humidity;
    private float pressure;

    public WeatherData(){

    }
    
    public void measurementsChanged(){
        setChanged();
        notifyObservers();
    }
    
    //通过这个方法自定义触发天气站的数据更新
    private 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;
    }
}

观察者实现

import java.util.Observable;
import java.util.Observer;

public class CurrentConditionsDisplay implements Observer {
    Observable observable;
    private float temperature;
    private float humidity;

    public CurrentConditionsDisplay(Observable observable) {
        this.observable = observable;
        observable.addObserver(this);
    }
    //观察者都要实现的方法
    @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 void display(){
        System.out.println("current condition : "+temperature+"F degrees and"+ humidity+"% humidity");
    }
}

看以上代码,我们思考几个问题:

  1. 如何让对象变成观察者
  2. 主题如何发出通知,采用什么方式拉还是推
  3. 观察者如何接收到通知 答案:
  4. 我们看观察者的构造方法 public CurrentConditionsDisplay(Observable observable),观察者首先要获取到主题对象,在自己决定进行的操作,这里我们调用addObserver方法来把观察者加入到主题的中。
  5. 首先main方法中调用setMeasurements,自定义模拟天气站的数据更新,在调用measurementsChanged方法来通知注册的观察者。
  6. 这里涉及到Observable两个方法,我们知道观察者有两种方式,一种是拉,一种是推,对应着其中两个方法。,第一个是notifyObservers(Object arg),采用的是推的方式。 Snipaste_2021-04-11_15-00-49.png 第二个采用的是拉的方式notifyObservers() image.png 所以我们应该看出其中的相同点啦,就是主题通知方法是否带数据,上面我们的示例代码就是采用拉的方式,可以参考一下。

3 设计原则

  1. 面向接口编程
  2. 多用组合,少用继承
  3. 封装变化
  4. 为交互对象之间的松耦合设计而努力

4 要点

  1. 观察者定义了对象之间一对多的关系
  2. 主题(可观察者)用一个共同的接口来更新观察者。
  3. 观察者和主题之间用松耦合方式结合,主题不知道观察者的细节,只知道观察者实现了观察者的接口。
  4. 使用此模式,你可以从被观察者处推或者拉数据(然而,推这个方式被认为更正确)
  5. 有多个观察者时,不可以依赖特定的通知顺序。
  6. java实现多种观察者模式的实现。包括通用的java.util.Observable.
  7. 要注意java.util.Observable实现上带来的问题,(面向实现的问题)
  8. 如果有必要的话自己实现Observable

5 扩展

  1. java观察者模式不好的地方-面向实现编程的坏处。
  2. 观察者模式中设置setChange的好处-增加程序的弹性