行为型——观察者模式

什么是观察者模式

观察者模式:定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会受到通知并自动更新。类似一种订阅发布模式,用户订阅某主题,当该主题有更新后会主动通知订阅者。

从定义就可以看出,观察者模式是一种行为模式,包含以下角色:

  1. 抽象主题:抽象主题接口,定义了添加观察者、删除观察者、通知观察者的方法。
  2. 具体主题:具体主题类,实现抽象目标中的通知方法,当具体主题类内部状态发生改变时,主动通知订阅者。
  3. 抽象观察者:抽象观察接口,所有具体观察者类都要实现该接口,定义了response方法,供具体主题调用。
  4. 具体观察者:实现抽象观察者中的response方法。

观察者模式的类图如下图所示:

为什么要用观察者模式

了解了观察者模式的一些定义后,观察者模式有什么用呢?什么场景下可能用到观察者模式?

在软件开发过程中,很多对象并不是独立存在的,有时候一个对象的行为发生变化,可能会同时影响多个其他对象。以《HeadFirst 设计模式》中的“气象站”为例:

有一个气象站用于获取温度、湿度、气压等天气信息,有3个布告板:目前状况、气象统计、气象预报。这3个布告板依赖于气象站获取到的气象信息,进行数据展示。当气象站获取到新的气象数据后,需要及时更新3个布告板中的信息。如下图所示:

如果不使用观察者模式,我们的代码可能是这样的:

这时用观察者模式就能更好地完成任务,不仅符合开闭原则,还能解耦气象站和布告板类。

观察者模式的优缺点可以总结如下:

  1. 优点
    • 解耦:降低了目标和观察者之间的耦合关系;
    • 符合依赖倒置原则。
  2. 缺点
    • 最主要的缺点可能是:当观察者很多时,通知的发布会花费很多时间,影响程序效率。

JDK中的Observable类

在java.util包中内置了Observer接口和Observable类,前者相当于抽象观察者,后者相当于抽象主题类。我们可以通过实现和继承Observer和Observable,快速实现观察者模式。不过,JDK9中被弃用了。

下面我们简单看下它们的源码:

1
2
3
4
package java.util;
public interface Observer {
void update(Observable o, Object arg);
}

和前面我们提到的观察者模式不同,Observable类中有一个changed字段表示主题状态是否发生变化,这给我们的设计可以带来更大的弹性。因为,我们可以更可控的去通知观察者。以上面的气象站为例,原本我们可能只要监测数据发生一丁点变化就要通知观察者,但是现在我们可以在数据变化超过一定范围时再通知观察者。

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
51
52
53
54
55
56
57
58
59
60
package java.util;

public class Observable {
private boolean changed = false;  //是否改变状态
private Vector obs;    //Vector利用同步方法来线程安全,线程安全在多线程情况下不会造成数据混乱

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) {
if (!changed)    //状态值未改变时返回,不通知
return;
arrLocal = obs.toArray();  //将Vector转换成数组
clearChanged();    //重置状态
}

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

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

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();
}
}

观察者模式的应用

应用场景

  1. 所有涉及到订阅发布的场景,都可能用到观察者模式;
  2. 对象间存在一对多关系,一个对象的状态发生改变会影响其他对象。

JDK中的应用

提供了Observer接口和Observable类。需要注意的是:

  1. 抽象主题Observable是类,而不是接口,因此不支持继承。
  2. 这两个类(接口)在JDK9中已经被弃用,引入了新的Flow相关类。

为什么弃用它?主要有以下几点原因:

  1. 支持的事件模型非常有限,只能单一的通知观察者数据发生更改;
  2. 不支持序列化;
  3. 通知的顺序未指定。

Spring中的应用

  1. Spring中的事件监听器,是很经典的观察者模式。

参考资料

  1. http://c.biancheng.net/view/1390.html
  2. 《HeadFirst设计模式》
打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2021-2022 Yin Peng
  • 引擎: Hexo   |  主题:修改自 Ayer
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信