设计模式:观察者模式
场景再现
学习设计模式的一个比较好的方式是在实际场景中来试着应用。首先我们还是来看一个实际的场景,很多公司都有自己的数据中心,我们试着定义一个数据中心的类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class DataCenter {
// ...
private Object employeeData;
private Object companyData;
private Object humanResourceData;
public Object getEmployeeData() {
// ...
return employeeData;
}
public Object getCompanyData() {
// ...
return companyData;
}
public Object getHumanResourceData() {
// ...
return humanResourceData;
}
// ...
}
有了这个类,再往下细分,每个部门的数据就有了着落:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class HumanResourceData {
private DataCenter dataCenter;
private Object data;
// ...
HumanResourceData(DataCenter dc) {
this.dataCenter = dc;
this.data = dc.getHumanResourceData();
}
public void refreshData() {
this.data = dc.getHumanResourceData();
}
public double getMonthlyExpense() {
// ...
return data.monthlyExpense;
}
public Object getInterviewPerformance() {
// ...
return data.interviewPerformance;
}
}
但这里有个问题,每个部门都希望能够感知 DataCenter
里数据的变化,这样他们能够基于此做出对应的调整。实现这个功能的做法有很多,最直接的就是每次只要一用到 HumanResourceData
里的数据,就运行 refreshData()
方法,这看上去可以解决数据实时性的问题,但还是存在一些不足:
- 如果数据类被频繁使用,那么
refreshData()
被频繁调用,DataCenter
对象也会被频繁访问,如果所有部门都这样做,那么数据中心无形之中会增加大量的访问 - 假如说现在一个数据中心需要拆分,拆分后的数据中心会负责不同部门的数据,也就是说每个部门需要到指定的数据中心去获取数据,这时我们就需要改动每个部门的类的定义
综合来看,这些问题的根源在于数据中心类与下面的部门数据类的关系不明确,数据中心就像一个公共资源,任何其他的类均可以访问。另外,下面的部门类需要自己去感知数据何时更新,可这个职责原本就是数据中心应该做的。
观察者模式
观察者模式,又被称为监听者模式,就是用来解决我们上面提到的对象之间的耦合性的问题。同时观察者模式定义了一个一对多的关系,当主对象的状态发生了变化,那么它的所有依赖对象就会被通知,被更新。
这里的主对象就是 Subject,依赖对象是 Observer。你可以把二者的关系想像成报社和订阅者,每当报社有新的内容出来,报社就会分发给它的订阅者,当某个订阅者取消订阅了,报社就将其移出,这样该订阅者将不再会收到订阅。
用观察者模式把我们前面的例子改写一下就变成了下面这样:
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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
interface Subject {
public void registerObserver(Observer ob);
public void removeObserver(Observer ob);
public void notifyObserver();
}
interface Observer {
public void update(Subject dc);
}
class DataCenter implements Subject {
private Set<Observer> observers = new HashSet<>();
private Object employeeData;
private Object companyData;
private Object humanResourceData;
public void registerObserver(Observer ob) {
observers.add(ob);
}
public void removeObserver(Observer ob) {
observers.remove(ob);
}
public void notifyObserver() {
float temp = getTemperature();
float humidity = getHumidity();
float pressure = getPressure();
for (Observer ob : observers) {
ob.update(temp, humidity, pressure);
}
}
public Object getEmployeeData() {
// ...
return employeeData;
}
public Object getCompanyData() {
// ...
return companyData;
}
public Object getHumanResourceData() {
// ...
return humanResourceData;
}
// ...
}
class HumanResourceData implements Observer {
private Subject dataCenter;
private Object data;
// ...
HumanResourceData(Subject dc) {
this.dataCenter = dc;
this.data = dc.getHumanResourceData();
}
public void update(Subject dc) {
if (dc instanceof DataCenter) {
this.dataCenter = dc;
this.data = dc.getHumanResourceData();
}
}
public double getMonthlyExpense() {
// ...
return data.monthlyExpense;
}
public Object getInterviewPerformance() {
// ...
return data.interviewPerformance;
}
}
那么对比之前的实现,在加上了观察者模式后具体有哪些改进呢?
首先,DataCenter
以及 HumanResourceData
不再互相依赖,它们转而去依赖接口(抽象)———— Subject
和 Observer
,这样,添加更多的 Subject 以及 Observer 实例,原先定义的 DataCenter
以及 HumanResourceData
均不会受到影响。
此外,DataCenter
仅知道 HumanResourceData
实现了 Observer
接口,HumanResourceData
也仅知道 DataCenter
是个 Subject
类型,然后里面有对应的方法拉取期望的数据,除此之外这两个类相互彼此再无依赖,这样大大降低了耦合。
最后,也是最重要的,HumanResourceData
无需关心数据什么时候更新,一旦数据更新,DataCenter
就会调用 HumanResourceData
内的 update
方法,将数据传入,HumanResourceData
只需将 update
按自己的期望实现即可,如何实现,DataCenter
也并不关心。这近乎完美地解决了对象间的数据共享与控制的问题。
同时,观察者模式也体现了很多的设计思想与原则,比如下面这些:
- 选择对象的组合而非继承(
DataCenter
与HumanResourceData
的关系通过组合来达成) - 面向抽象(接口)编程,而非具体实现(面向
Subject
与Observer
来建立相互之间的联系) - 将变化的部分与不变的部分分离(
update
方法内定义变化的部分,这样其余部分不受影响)
另一种实现方式
观察者模式是一个应用非常广泛的模式,Java 的 java.util
类库中都有对应可以直接拿来用的接口,还是之前的例子,我们应用 Java 自带的类再实现一遍:
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
import java.util.Observable;
import java.util.Observer;
class DataCenter extends Observable {
private Set<Observer> observers = new HashSet<>();
private Object employeeData;
private Object companyData;
private Object humanResourceData;
public Object getEmployeeData() {
// ...
return employeeData;
}
public Object getCompanyData() {
// ...
return companyData;
}
public Object getHumanResourceData() {
// ...
return humanResourceData;
}
}
class HumanResourceData implements Observer {
private Object data;
// ...
HumanResourceData(Observable dc) {
observable.addObserver(this);
this.data = observable.getHumanResourceData();
}
public void update(Observable ob, Object arg) {
if (ob instanceof DataCenter) {
DataCenter dc = (DataCenter)ob;
this.data = dc.getHumanResourceData();
}
}
public double getMonthlyExpense() {
// ...
return data.monthlyExpense;
}
public Object getInterviewPerformance() {
// ...
return data.interviewPerformance;
}
}
自从 Java 9 开始,Observable
与 Observer
已经 deprecated 了,原因在于 Observable
是一个类而非接口,Java 中并不支持多重继承,因而 Observable
的子类就不能再继承其他的父类了,此外,Observable
中有几个方法还是 protected 的,也就是只有子类才能调用,这多少有失灵活性。
不论如何,这背后的思想是不变的,依然是我们上面讲的观察者模式。