盒子
盒子
文章目录
  1. 观察者模式
    1. 类图
  2. 要点
  3. 例子
    1. 使用java内置的观察者模式:

设计模式之观察者模式

观察者模式

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

类图

观察者类图

观察者接口Observer,观察对象发生变化后,update()方法会被调用
被观察者接口Subject,通过Notify()方法通知观察者


要点

  • 观察者模式定义了对象之间一对多的关系
  • 主题(也就是可观察者)用一个共同的接口来更新观察者
  • 观察者和可观察者之间用松耦合方式结合,可观察者不知道观察者的细节,只知道观察者实现了观察者接口
  • 使用此模式时,你可以被观察者 推(push) 或 拉(pull)数据。(然而,推的方式被认为更“正确”)
  • 有多个观察者时,不可以依赖特定的通知次序
  • java有多种观察者模式的实现,包括通用的java.util.Observable
  • 有必要的话,可以实现自己的Observable

例子

一个气象站的例子。我们有一个类,提供三个方法,分表用来获取 getTemperature()温度,getHumidity()湿度,getPressure()气压。一旦气象测量更新,measurementsChanged()方法就会被调用。
我们的工作是:实现measurementsChanged()方法,在气象更新之后获取最新的数据,推给三个布告板

我们先看个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class WeatherData{
public void measurementsChanged(){
float temp = getTemperature();//获得温度
float humidity = getHumidity();//获得湿度
float pressure = getPressure();//获得气压

//更新一号布告板
currentConditionsDisplay.update(temp,humidity,pressure);
//更新二号布告板
statisticsDisplay.update(temp,humidity,pressure);
//更新三号布告板
forecastDisplay.update(temp,humidity,pressure);
}
}

存在的问题:

  • 这是针对具体实现编程,而不是针对接口
  • 对于每个新的布告板,我们都得修改代码
  • 我们无法在运行时动态增加(或删除)布告板
  • 布告板没有实现一个共同的接口
  • 我们尚未封装改变的部分
  • 我们侵犯了WeatherData类的封装

设计原则:为了交互对象之间的松耦合设计而努力

我们使用观察者模式来设计
首先我们定义观察者模式接口

1
2
3
4
5
6
7
8
9
10
11
//观察者
public interface Observer {
public abstract void update(float temp,float humidity,float pressure);
}

//被观察者
public interface Subject{
void registerObserver(Observer o);
void removeObserver(Observer o);
void notifyObservers();//通知观察者
}

我们来建立具体的类
观察者1

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 WeatherData implements Subject {
//观察者集合
private ArrayList<Observer> observers;
private float temp;
private float humidity;
private float pressure;

public WeatherData(){
observers = new ArrayList<Observer>();
}
//增加观察者
public void registerObserver(Observer o) {
observers.add(o);
}
//删除观察者
public void removeObserver(Observer o) {
int i = observers.indexOf(o);
if(i >= 0){
observers.remove(i);
}
}
//通知观察者
public void notifyObservers() {
for(Observer o : observers){
o.update(temp,humidity,pressure);
}
}
//气象更新,需要通知观察者
public void measurementsChanged(){
notifyObservers();
}
//测试接口
public void setMeasurements(float temp,float humidity,float pressure){
this.temp = temp;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
}
}

具体布告板,其他布告板类似
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//一号布告板
public class CurrentConditionsDisplay implements Observer,DisplayElement {

private Subject weatherData;
private float temp;
private float humidity;

public CurrentConditionsDisplay(Subject weatherData){
this.weatherData = weatherData;
weatherData.registerObserver(this);
}
//收到通知之后,进行操作
public void update(float temp, float humidity, float pressure) {
this.temp = temp;
this.humidity = humidity;
display();
}

public void display(){
System.out.println("display");
}
}

测试
1
2
3
4
5
6
7
8
9
10
11
public class WeatherStation{
public static void main(String[] args){
WeatherData weatherData = new WeatherData();
//定义观察者
CurrentConditionsDisplay conditionsDisplay = new CurrentConditionsDisplay(weatherData);
//可以定义其他观察者....

//更新数据,观察者更新
weatherData.setMeasurements(10,20,30);
}
}

使用java内置的观察者模式:

java内置的观察者模式 java.util.Observer 和 java.util.Observable
观察者2

具体代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class WeatherData extends Observable {
private float temp;
private float humidity;
private float pressure;

public WeatherData(){}

//气象更新,需要通知观察者
public void measurementsChanged(){
setChanged();
notifyObservers();
}
//测试接口
public void setMeasurements(float temp,float humidity,float pressure){
this.temp = temp;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
}
}

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
//一号布告板
public class CurrentConditionsDisplay implements java.util.Observer,DisplayElement {

private Observable observable;
private float temp;
private float humidity;

public CurrentConditionsDisplay(Observable observable){
this.observable = observable;
observable.addObserver(this);
}
//收到通知之后进行操作
public void update(Observable o, Object arg) {
if(o instanceof WeatherData){
WeatherData weatherData = (WeatherData) o;
this.temp = temp;
this.humidity = humidity;
display();
}
}

public void display(){
System.out.println("display");
}
}

我们在使用 java.util.Observable 需要注意的问题:

  • Observable是一个类,而不是接口。我们只能通过继承来使用
  • 多个观察者被通知的次序是不固定的