Observer 觀察者

在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 SimpleObserverEx 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方法,那這個辦法就無法使用

 

本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/zhouyongyang621/archive/2010/07/20/5750702.aspx

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章