观察者模式大家应该十分熟悉,贴一张别人的图如下:
上图十分清晰的展示了观察者模式是什么样的。在java的util包里,实际上有内置的对观察者模式的实现,java.uti.Observable类就是这里的主题,而Observer接口对应的就是Observer观察者接口。
但是,实际上,虽然java对Observable的实现不是使用接口来实现,因此,实际上并不是很符合设计模式的多用组合,少用继承的原则,而且,其对应于setSate()方法也就是setChanged()方法访问权限为setChanged,因此,很难不通过继承来将其组合到自己的代码中去,但是,其对观察者模式的实现毕竟还是十分的严密而规范的,下面是我对Observable类的说明。
public class Observable {
private boolean changed = false;
private Vector obs;
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();
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();
}
}
可以看出,通过bool类型变量changed的赋值,可以判断主题是否更新,以作为是否将信息推送给观察者的依据。changed的改变必需是线程安全的,这也是为什么setChanegd方法和clearChanged方法都用synchronized关键字声明。
使用了Vector容器来保存观察者的引用,而不是ArrayList,同样是出于线程安全的考虑,ArrayList线程不安全,Vector线程安全。
public void notifyObservers(Object arg) {
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);
}
上述代码段中,出于线程安全的需要,在将obs转为数组时,需要用同步控制块来做处理,这也是为了防止由于多个观察者线程并发对obs改变造成的线程异常,尽管这里是线程安全的,但是jdk源码的注释中指出,这里有可能出现刚刚加入的观察者无法通知到更新或者刚刚删除的观察者接收到了不该接受到的消息的情况。