观察者设计模式(Observer Pattern)

描述: 当对象存在一对多的关系时,使用观察者设计模式。比如一个对象被修改时,则会通知依赖他的对象。观察者设计模式属于行为行设计模式

核心思想: 当一个对象发生变化时通知其他依赖它的对象

使用场景:

  • 一个对象改变一个或多个对象也发生变化,而不知道有多少对象发生变化,降低对象之间的耦合度。

  • 一个对象必须通知其他的对象,而不知道这些对象都有谁

  • 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制

  • 一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。

观察者设计模式优点:

  • 观察者和被观察者时抽象解耦的
  • 建立一套触发机制

观察者设计模式缺点:

  • 如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
  • 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
  • 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。

注意事项:

  • JAVA 中已经有了对观察者模式的支持类
  • 避免循环引用
  • 如果顺序执行,某一观察者会导致系统卡壳,一般采用异步的方式

示例:

现有一个需求,有一个天气预报系统,获取天气对象,三个字段,温度 , 湿度, 气压,当它们的数值发生变化时通知到布告板展现出来

  • 抽象出主题接口对象
1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 主题(发布者、被观察者)
*/
interface Subject {
//注册一个观察者进来
void registerObserver(Observer o);

//删除一个观察者
void removeObserver(Observer o);

//通知其他观察者
void notifyObservers();
}
  • 抽象观察者的对象
1
2
3
interface Observer {   
void update(float temp, float humidity, float pressure);
}
  • 抽象布告对象
1
2
3
interface DisplayElement {    // 更换布告
void display();
}
  • 被观察者的具体实现
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
43
44
45
46
47
48
49
50
class WeatherData implements Subject {
private ArrayList observers;
private float temperature;//温度
private float humidity;//湿度
private float pressure;//气压

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

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

/**
* 删除观察者
*/
@Override
public void removeObserver(Observer o) {
int i = observers.indexOf(o);
if (i >= 0) {
observers.remove(i);
}
}

/**
* 通知各个观察者
*/
@Override
public void notifyObservers() {
for (int i = 0; i < observers.size(); i++) {
Observer observer = (Observer) observers.get(i);
observer.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();
}
}
  • 显示当前天气的公告牌CurrentConditionsDisplay
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class CurrentConditionsDisplay implements Observer, DisplayElement {
private float temperature;
private float humidity;
private float pressure;
@Override
public void update(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
display();
}

@Override
public void display() {
System.out.println("当前温度为:" + this.temperature + "℃");
System.out.println("当前湿度为:" + this.humidity);
System.out.println("当前气压为:" + this.pressure);
}
}
  • 只显示温度和湿度的公告牌
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

class Current implements Observer, DisplayElement {
private float temperature;
private float humidity;

@Override
public void update(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
display();
}

@Override
public void display() {
System.out.println("当前温度为:" + this.temperature + "℃");
System.out.println("当前湿度为:" + this.humidity);
}
}
  • 客户端调用
1
2
3
4
5
6
7
8
public static void main(String[] args) {
WeatherData weatherData = new WeatherData();
weatherData.registerObserver(new CurrentConditionsDisplay());
weatherData.registerObserver(new Current());
weatherData.setMeasurements(80, 65, 30.4f);
weatherData.setMeasurements(82, 70, 29.2f);
weatherData.setMeasurements(78, 90, 29.2f);
}

附图:

观察者设计模式UML