在
Java
中通过
Observable
类和
Observer
接口实现了观察者模式。一个
Observer
对象监视着一个
Observable
对象的变化,当
Observable
对象发生变化时,
Observer
得到通知,就可以进行相应的工作。例如在文档
/
视图结构中,文档被修改了,视图就会得到通知。
java.util.Observable
中有两个方法对
Observer
特别重要,一个是
setChange()
方法用来
设置一个内部标志位注明数据发生了变化;一个是
notifyObservers()
方法会去调用一个列表中所有的
Observer
的
update()
方法,通知它们数据发生了变化。
Observer
通过
Observable
的
addObserver()
方法把自己添加到这个列表中。这个列表虽然由
Observable
拥有,但
Observable
并不知道到底有哪些
Observer
正在观察等待通知。
Observable
只提供一个方法让
Observer
能把自己添加进列表,并保证会去通知
Observer
发生了变化。通过这种机制,可以有任意多个
Observer
对
Observable
进行观察,而不影响
Observable
的实现。
一个简单例子:
import java.util.Observable;
public class SimpleObservable extends Observable
{
private int data = 0;
public int getData(){
return data;
}
public void setData(int i){
if(this.data != i){ this.data = i; setChange();}
notifyObservers();
//
只有在
setChange()
被调用后,
notifyObservers()
才会去调用
update()
,否则什么都不干。
}
}
}
import java.util.Observable;
import java.util.Observer;
public class SimpleObserver implements Observer
{
public SimpleObserver(SimpleObservable so){
so.addObserver(this );
}
public void update(Observable o,Object arg/*
任意对象,用于传递参数
*/){
System.out.println(“Data has changed to” + (SimpleObservable)o.getData());
}
}
public class SimpleTest
{
public static void main(String[] args){
SimpleObservable
doc = new
SimpleObservable
();
SimpleObserver
view = new
SimpleObserver
(doc);
doc.setData(1);
doc.setData(2);
doc.setData(2);
doc.setData(3);
}
}
Data has changed to 1
Data has changed to 2 //
第二次
setData(2)
时由于没有
setChange
,所以
update
没被调用
Data has changed to 3
Observable
类有两个私有变量。一个
boolean
型的标志位,
setChange()
将它设为真,只有它为真时,
notifyObservers
方法才会调用
Observer
的
update
方法,
clearChange()
设标志位为假,
hasChange
返回当前标志位的值。另一个是一个
Vector
,保存着一个所有要通知的
Observer
列表,
addObserver
添加
Observer
到列表,
deleteObserver
从列表中删除指定
Observer
,
deleteObservers
清空列表,
countObservers
返回列表中
Observer
的数目,在
Observer
对象销毁前一定要用
deleteObserver
将其从列表中删除,不然因为还存在对象引用的关系,
Observer
对象不会被垃圾收集,造成内存泄漏,并且已死的
Observer
仍会被通知到,有可能造成意料外的错误,而且随着列表越来越大,
notifyObservers
操作也会越来越慢。
Observable
的所有方法都是同步的,保证了在一个线程对其标志位、列表进行操作时,不会有其它线程也在操作它。
Observable
的
notifyObservers(Object obj)
形式可以再调用
update
时将参数传进去。
通知顺序通常时越晚加入列表的越先通知。
update
会被依次调用,由于一个
update
返回后下一个
update
才被调用,这样当
update
中有大量操作时,最好将其中的工作拿到另一个线程或者
Observer
本身同时也是一个
Thread
类,
Observer
先挂起,在
update
中被唤醒,这样会有一个隐患,
Observer
线程还没来得及挂起,
update
就被调用了,通知消息就这样被错过了,一种解决办法是在
Observer
中实现一个同步的队列结构,并有一个类来封装参数,
update
实现一个参数类的对象把接收到的通知消息的参数封装在里面,然后把其加进队列,
run
方法从队列中移除参数对象,并进行处理,这保证了没有通知信息被丢失。
在多线程时,只有
Observer
会与单线程不同,
Observable
不需任何改变来支持多线程,因为它又很多作为单独线程运作的
Observer
。
一个多线程例子:
import java.util.*;
public class
SimpleObserver
Ex extends Thread implements Observer
{
Queue queue;//利用一个消息队列来接收Observable的通知,保证消息不会丢失
public void run(){
while(true){
ArgPack a = (ArgPack) queue.dequeue();
Observable o = a.o;
Object obj = a.arg;
//…执行相应的工作
}
}
public void update(Observable o, Object arg){
ArgPack a = new ArgPack (o,arg);
queue.queue(a);
}
}
import java.util.*;
public class Queue extends LinkedList{
public synchronized void queue(Object o){
addLast(o);
notify();
}
public synchronized void dequeue(){
while(this.isEmpty()){
try{
wait();
}
catch(InterruptedException el){
}
}
Object o;
try{
o =this.removeFirst();
}
catch(NoSuchElementException){
o = null;
}
return o;
}
}
public class ArgPack
{
public Observable o;
public Object obj;
public ArgPack (Observable o,Object obj){
this.o = o;this.obj = obj;
}
}
Observable
是一个类而不是一个接口这限制了它的使用,一个解决的办法是在一个
Observable
类中把我们的类包装进去
(
把我们的类实例当作
Observable
的域
),
因为
Observable
中的
setChange
是一个
protected
方法,我们没法在外面调用它。所以没法在我们的类中包装一个
Observable
,但是如果我们的类中同样也有
protected
方法,那这个办法就无法使用。