观察者模式定义
观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
设计原则:为了交互对象之间的松耦合设计而努力。
类图(待补充)
- 抽象主题(Subject)角色:主题角色把所有的观察者对象的引用保存在一个列表里;每个主题都可以有任何数量的观察者。主题提供一个接口可以加上或撤销观察者对象;
- 抽象观察者(Observer)角色:为所有的具体观察者定义一个接口,在得到通知时更新自己;
- 具体主题(ConcreteSubject)角色:保存对具体观察者对象有用的内部状态;在这种内部状态改变时给其观察者发出一个通知;
- 具体观察者(ConcreteObserver)角色:保存一个指向具体主题对象的引用;和一个与主题的状态相符的状态。具体观察者角色实现抽象观察者角色所要求的更新自己的接口,以便使本身的状态与主题的状态自恰。
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 interface Subject{
void registerObserver(Observer o);
void removeObserver(Observer o);
void notifyObservers();
}
// 一个具体的主题总是实现主题接口,除了注册和撤销方法之外,具体主题还实现了notifyObservers,此方法用于在状态改变时更新所有当前观察者
public class ConcreteSubject implements Subject {
private List<Observer> observersList = new ArrayList<Observer>();
@Override
public void registerObserver(Observer o) {
observersList.add(o);
}
@Override
public void removeObserver(Observer o) {
observersList.remove(o);
}
@Override
public void notifyObservers() {
for(Observer observer : observersList) {
observer.update();
}
}
}
// 所有潜在的观察者必须实现观察者接口,这个接口只有update()一个方法,当主题状态改变时它被调用
public interface Observer {
void update();
}
// 具体的观察者可以是实现此接口的任意类。观察者必须注册具体主题,以便接收更新
public class ConcreteObserver implements Observer {
@Override
public void update() {
}
}
|
Java 语言提供的观察者
视窗系统的事件模型采用观察者模式,因此观察者模式在Java语言里的地 位较为重要。
java.util库里面,提供了一个Observable类以及一个Observer接口,构成Java语言对观察者模式的支持。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public interface Observer {
/**
* This method is called whenever the observed object is changed. An
* application calls an <tt>Observable</tt> object's
* <code>notifyObservers</code> method to have all the object's
* observers notified of the change.
*
* @param o the observable object.
* @param arg an argument passed to the <code>notifyObservers</code>
* method.
*/
void update(Observable o, Object arg);
}
|
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
|
public class Observable {
private boolean changed = false;
private Vector<Observer> obs;
/** Construct an Observable with zero Observers. */
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) {
/*
* 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);
}
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();
}
}
|
总结
优点
- 观察者模式在被观察者和观察者之间建立一个抽象的耦合。被观察者角色所知道的只是一个具体观察者列表,每一个具体观察者都符合一个抽象观察者的接口。
- 观察者模式支持广播通讯。被观察者会向所有的登记过的观察者发出通知
不足
- 如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
- 如果在被观察者之间有循环依赖的话,被观察者会触发它们之间进行循环调用,导致系统崩溃。在使用观察者模式是要特别注意这一点。
- 如果对观察者的通知是通过另外的线程进行异步投递的话,系统必须保证投递是以自恰的方式进行的。
- 虽然观察者模式可以随时使观察者知道所观察的对象发生了变化,但是观察者模式没有相应的机制使观察者知道所观察的对象是怎么发生变化的。
其他
观察者模式可以使用备忘录模式(Memento Pattern)暂时将观察者对象存储在被观察者对象里面。