概述
观察者模式(Observer Pattern)是一个非常有用的模式,在Java语言当中,有许多地方用到了这一模式,比如Swing当中的事件模型。这一模式其实是建立在观察者模式基础之上的。这篇文章就来看看这个模式的一些基本的东西。
定义:
这个模式主要用来定义对象之间的一种一对多的依赖关系。当一个对象的状态发生改变的时候,所以依赖于它的对象得到通知并且自动更新。也称为发布-订阅模式(Publish-Subscribe)。这种模式使得对象之间的关系非常松,也就是松耦合。如下图:
结构:
该模式中的四个参与者分别为:目标(Subject)、具体目标(Concrete Subject)、观察者(Observer)、具体观察者(Concrete Observer)。
分别介绍如下:
- Subject:Subject知道它的观察者,一个目标可以有多个观察者。(其实,一个观察者也可以对应多个目标,因此定义中一对多的说法并不是很准确。Subject还提供注册和删除观察者对象的接口。
- Concrete Subject: 将观察者感兴趣的状态存入观察者对象,当状态改变的时候,通知各个观察者。
- Observer: 定义一个更新接口
- Concrete Observer: 维护一个指向ConcreteSubject对象的引用,存储有关的状态,实现Observer接口中的更新接口。
Java类库中的实现:
Java类库中对观察者模式提供的支持并没有完全按照上面的描述。
Observer接口:
对于观察者,它提供了一个Observer接口,位于java.util包中,之定义了一个update方法。如下:
void update(Observable o, Object arg);
有2个参数,其中Observable是目标的引用,可以通过这一引用获取目标的状态或者进行其他操作。Object是一个与具体实现逻辑相关的对象引用,也用于传递操作相关的信息。
Observable类:
Java中没有提供目标的接口,而是直接提供一个类。这样做有好有坏,我们就可以省去许多代码,同时也失去了一定的灵活性。来看一下这个类提供的各个方法;
注册、删除观察者:
以下三个方法用于注册、删除观察者,返回观察者数量等。
public synchronized void addObserver(Observer o) {
if (o == null)
throw new NullPointerException();
if (!obs.contains(o)) {
obs.addElement(o);
}
}
这个类采用Vector类型(私有与obs)来维护所有观察者的引用。很明白,增加一个观察者。
public synchronized void deleteObservers() {
obs.removeAllElements();
}
public synchronized void deleteObservers() {
obs.removeAllElements();
}
死一个方法删除一个观察者,后一个方法删除全部观察者。一样简单明了。
public synchronized int countObservers() {
return obs.size();
}
返回观察者数量,没什么可说的。
有关目标状态的几个方法:
通过私有的布尔类型变量changed来判断、操作。
public synchronized boolean hasChanged() {
return changed;
}
查看当前目标状态是否改变。
protected synchronized void setChanged() {
changed = true;
}
设置状态改变标志位true。
protected synchronized void clearChanged() {
changed = false;
}
清空状态改变标志位。
注意一下各个方法的访问控制符,具体实现的时候小心使用。
通知观察者:
这些方法通知观察者。其实就是调用观察者的update方法,并将改变标志位清空。通知所有观察者,可以传递一个对象用于交互。如下:
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 void notifyObservers() {
notifyObservers(null);
}
一样很明了,比较麻烦的就是多线程的问题了,不多说。
一个简单例子:
下面是一个最简单的例子。DataStore作为目标,Screen作为观察者,同时有一个特殊的观察者SpecialScreen,这个观察着在第一次得到通知之后就把自己从观察者中删除。
DataStore:
import java.util.Observable;
/**
* @author Brandon B. Lin
*
*/
public class DataStore extends Observable {
private String data;
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
/*mark the abservable as changed*/
setChanged();
}
}
Screen:
import java.util.Observable;
import java.util.Observer;
/**
* @author Brandon B. Lin
*
*/
public class Screen implements Observer {
protected int observerId;
public Screen(int observerId) {
this.observerId = observerId;
}
@Override
public void update(Observable o, Object arg) {
// act on the update
System.out.println(observerId + ": The subject has changed!");
}
}
SpecialScreen:
import java.util.Observable;
public class SpecialObserver extends Screen {
public SpecialObserver(int observerId) {
super(observerId);
}
@Override
public void update(Observable o, Object arg) {
// act on the update
System.out.println(observerId
+ ": The subject has changed! and I will leave!");
o.deleteObserver(this);
}
}
Test:
/**
* @author Brandon B. Lin
*
*/
public class Test {
/**
* @param args
*/
public static void main(String[] args) {
DataStore dataStore = new DataStore();
Screen[] screens = new Screen[10];
for (int i = 0; i < 10; i++) {
if (i == 5) {
screens[i] = new SpecialObserver(i);
} else {
screens[i] = new Screen(i);
}
dataStore.addObserver(screens[i]);
}
dataStore.setData("new data");
dataStore.notifyObservers();
dataStore.setData("Latest data");
dataStore.notifyObservers();
}
}
参考资料:
《设计模式:可复用面向对象软件的基础》
JDK源码
声明:版权所有,转载务必注明出处,本作者保留一切法律权利。