(六)观察者模式详解(包含观察者模式JDK的漏洞以及事件驱动模型) (二)

2014-11-24 10:41:13 · 作者: · 浏览: 10
//notifyObservers(Object arg)的重载方法
public void notifyObservers() {
notifyObservers(null);
}
//通知所有观察者,被观察者改变了,你可以执行你的update方法了。
public void notifyObservers(Object arg) {
//一个临时的数组,用于并发访问被观察者时,留住观察者列表的当前状态,这种处理方式其实也算是一种设计模式,即备忘录模式。
Object[] arrLocal;
//注意这个同步块,它表示在获取观察者列表时,该对象是被锁定的
//也就是说,在我获取到观察者列表之前,不允许其他线程改变观察者列表
synchronized (this) {
//如果没变化直接返回
if (!changed)
return;
//这里将当前的观察者列表放入临时数组
arrLocal = obs.toArray();
//将改变标识重新置回未改变
clearChanged();
}
//注意这个for循环没有在同步块,此时已经释放了被观察者的锁,其他线程可以改变观察者列表
//但是这并不影响我们当前进行的操作,因为我们已经将观察者列表复制到临时数组
//在通知时我们只通知数组中的观察者,当前删除和添加观察者,都不会影响我们通知的对象
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();
}
}
被观察者除了一点同步的地方需要特殊解释一下,其余的相信各位都能看明白各个方法的用途。其实上述JDK的类是有漏洞的,或者说,在我们使用观察者模式时要注意一个问题,就是notifyObservers这个方法中的这一段代码。

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

for (int i = arrLocal.length-1; i>=0; i--)
((Observer)arrLocal[i]).update(this, arg); 在循环遍历观察者让观察者做出响应时,JDK没有去抓取update方法中的异常,所以假设在这过程中有一个update方法抛出了异常,那么剩下还未通知的观察者就全都通知不到了,所以LZ个人比较疑惑这样的用意(LZ无法想象JAVA类库的制造者没考虑到这个问题),是sun当时真的忘了考虑这一点,还是另有它意?当然各位读者如果有自己的见解可以告知LZ,不过LZ认为,不管是sun如此做是别有用意,还是真的欠考虑,我们都要注意在update方法里一定要处理好异常,个人觉得JDK中比较保险的做法还是如下这样。

[java]
for (int i = arrLocal.length-1; i>=0; i--){
try {
((Observer)arrLocal[i]).update(this, arg);
} catch (Throwable e) {e.printStackTrace();}
}

for (int i = arrLocal.length-1; i>=0; i--){
try {
((Observer)arrLocal[i]).update(this, arg);
} catch (Throwable e) {e.printStackTrace();}
} 这样无论其中任何一个update是否成功都不会影响其余的观察者进行更新状态,我们自己比较保险的做法就是给update方法整个加上try块,或者确认不会发生运行时异常。

观察者模式不是特别复杂,但考虑到初学者,我们先做一个非常简单的例子。就拿上述小说网的例子,我们做一个DEMO。

首先要搞清楚在读者和作者之间是谁观察谁,很明显,应该是读者观察作者。所以作者是被观察者,读者是观察者,除了这两个类之外,我们还需要额外添加一个管理器帮我们管理下作者的列表便于读者关注,于是一个观察者模式的DEMO就出现了。如下,首先是读者类,LZ在各个类都加了点注释。

[java]
//读者类,要实现观察者接口
public class Reader implements Observer{

private String name;

public Reader(String name) {
super();
this.name = name;
}

public String getName() {
return name;
}

//读者可以关注某一位作者,关注则代表把自己加到作者的观察者列表里
public void subscribe(String writerName){
WriterManager.getInstance().getWriter(writerName).addObserver(this);
}

//读者可以取消关注某一位作者,取消关注则代表把自己从作者的观察者列表里删除
public void unsubscribe(String writerName){
WriterManager.getInstance().getWriter(writerName).deleteObserver(this);
}

//当关注的作者发表新小说时,会通知读者去看
public void update(Observable o, Object obj) {