观察者模式

问题

1
2
3
4
气象站可以将每天测量到的温度,湿度,气压等等以公告的形式发布出去(比如发布到自己的网站或第三方)。
而要设计开放型API,便于其他第三方也能接入气象站获取数据。
提供温度、气压和湿度的接口
浏量数据更新时,要能实时的通知给第三方

基本原理

观察者模式类似订牛奶业务1

  1. 奶站/气象局:Subject

  2. 用户/第三方网站:Observer

Subject:登记注册、移除和通知

观察者模式的结构图
  1. 抽象主题(Subject)角色:也叫抽象目标类,它提供了一个用于保存观察者对象的聚集类和增加、删除观察者对象的方法,以及通知所有观察者的抽象方法
  2. 具体主题(Concrete Subject)角色:也叫具体目标类,它实现抽象目标中的通知方法,当具体主题的内部状态发生改变时,通知所有注册过的观察者对象。
  3. 抽象观察者(Observer)角色:它是一个抽象类或接口,它包含了一个更新自己的抽象方法,当接到具体主题的更改通知时被调用。
  4. 具体观察者(Concrete Observer)角色:实现抽象观察者中定义的抽象方法,以便在得到目标的更改通知时更新自身的状态。

解决问题

  1. 接口,主题

    1
    2
    3
    4
    5
    public interface Subject {
    public void registerObserver(Observer o);
    public void removeObserver(Observer o);
    public void notifyObserver();
    }
  2. 观察者接口

    1
    2
    3
    public interface Observer {
    public void update(Data data);
    }
  3. 具体主题,具体主题天气信息包含一个天气数据,和观察者列表,当数据发生变化是通知观察者。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    public class Data {
    private float temperature;
    private float pressure;
    private float humidity;

    public Data(float temperature, float pressure, float humidity) {
    this.temperature = temperature;
    this.pressure = pressure;
    this.humidity = humidity;
    }

    public float getTemperature() {
    return temperature;
    }

    public void setTemperature(float temperature) {
    this.temperature = temperature;
    }

    public float getPressure() {
    return pressure;
    }

    public void setPressure(float pressure) {
    this.pressure = pressure;
    }

    public float getHumidity() {
    return humidity;
    }

    public void setHumidity(float humidity) {
    this.humidity = humidity;
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    public class WeatherData implements Subject{

    //观察者集合
    private ArrayList<Observer> observers;

    //数据
    private Data data;

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

    //当有新的数据更新时,调用
    public void setData(float temperature,float pressure,float humidity){
    this.data = new Data(temperature,pressure,humidity);
    notifyObserver(); //通知观察者
    }

    //注册观察者
    @Override
    public void registerObserver(Observer o) {
    observers.add(o);
    }

    //移除观察者
    @Override
    public void removeObserver(Observer o) {
    if (observers.contains(o)) {
    observers.remove(o);
    }
    }

    /**
    * 通知观察者
    */
    @Override
    public void notifyObserver() {
    for (int i = 0; i <observers.size(); i++){
    observers.get(i).update(data);
    }
    }
    }
  4. 观察者,数据发生变化时进行update操作

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public class CurrentConditions implements Observer{

    //温度,气压,湿度
    private Data data;

    @Override
    public void update(Data data) {
    this.data = data;
    display();
    }

    public void display(){
    System.out.println("temperature:"+data.getTemperature());
    System.out.println("pressure:"+data.getPressure());
    System.out.println("humidity:"+data.getHumidity());
    }
    }
  5. 客户端

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public class Client {
    public static void main(String[] args) {

    //创建一个主题
    WeatherData weatherData = new WeatherData();

    //创建观察者
    CurrentConditions currentConditions = new CurrentConditions();

    //注册到weatherData
    weatherData.registerObserver(currentConditions);

    //测试
    weatherData.setData(10, 100, 30);

    }
    }

观察者模式设计后,会以集合的方式来管理用户(Observer),包括注册,移除和通知。

这样,我们增加观察者(这里可以理解成一个新的公告板),就不需要去修改核心类WeatherData不会修改代码,遵守了ocp原则。

源码分析

Observable源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class Observable {
private boolean changed = false;
private Vector<Observer> obs;
//添加
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) {
/*
* a temporary array buffer, used as a snapshot of the state of
* current Observers.
*/
Object[] arrLocal;

synchronized (this) {

if (!changed)
return;
arrLocal = obs.toArray();
clearChanged();
}

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

JDK的Observable对象中使用Vector存放观察者对象。观察者接口如下:

1
2
3
4
public interface Observer {

void update(Observable o, Object arg);
}