Favor Soup | 反胃书屋

斯是陋室,惟吾德馨。
posts - 34, comments - 18, trackbacks - 0, articles - 0
  IT博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

Observable与Observer

Posted on 2007-03-08 14:28 D主 阅读(201) 评论(0)  编辑 收藏 引用 所属分类: Java Related
    在 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 方法,那这个办法就无法使用。


只有注册用户登录后才能发表评论。