使用觀察者(Observer)實現對象監聽
Bromon原創 請尊重著作權
有非常多的時候,我們希望自己的程式能夠監視資料的變化,然後做出響應,這種情況非常多,比如探測資料庫中資料的變化、檢測使用者狀態的變化等等。通常我們都缺乏一種雙工通訊的機制,只能選擇讓程式做論詢,隔一段時間檢測一次資料變化,記錄下來與上一次檢測結果做對比,從而判斷資料是否發生了變化。毫無疑問這樣的方式很笨拙,不僅寫起來痛苦,跑起來也耗資源,是典型的用80%的時間解決20%的問題。
觀察者(Observer)是一種模式,也是Java中的一個API,它讓一個值對象(Value Object)具備自省的功能,當他發現自己的狀態改變了,就向相關的對象發送訊息,這樣的監聽方式當然比輪詢好。我感冒了自己會去醫院,用不著醫生每個月來問一次。禽獸·宇楓曾經給了我一段麻將遊戲的伺服器端代碼,本來是讓我研究一下麻將的演算法,但是卻被其中Observer的使用所吸引,這樣寫出來的伺服器執行效率很高。我曾經用線程池+反射+觀察者寫了一個立即訊息的伺服器,既有socket的方便也具備udp的高效,可惜後來因為大幅修改設計代碼作廢了,不過觀察者還是值得研究。
Java的Observer API是對觀察者模式的一個實現。假設我們有一個對象容器,其中存放使用者訊息,我希望這個容器自省,當有新的訊息進來就自動觸發觀察者作出響應。首先定義訊息對象,是個很簡單的值對象:
package com.gwnet.smsMessenger.mm.bromon;
public class Message
{
private int id;
private String sender;
private String receiver;
private String content;
private String time;
//請自己實現set/get方法,比如:
public int getId()
{
return id;
}
public void setId(int id)
{
this.id=id;
}
}
然後寫一個存放Message的容器,容器使用ArrayList來存放對象是個很好的選擇,也很簡單:
/*
* Created on 2004-8-11
*/
package com.gwnet.smsMessenger.mm.bromon;
import java.util.*;
/**
* @author Bromon
*/
public class MessageList extends Observable
{
private List m=new ArrayList();
private static MessageList ml=null;
public MessageList()
{
}
public static MessageList getInstance()
{
if(ml==null)
{
ml=new MessageList();
}
return ml;
}
public void add(Message msg)
{
m.add(msg);
super.setChanged();
super.notifyObservers(m);
}
public void del(Message msg)
{
m.remove(msg);
}
}
這個類繼承了Observable類,並且對其中的add方法做了手腳,很明顯,add方法的作用是向ArrayList容器中放入一個對象,這正是我們想監聽的操作,所以有了:
uper.setChanged();
super.notifyObservers(m);
這意思是一旦調用add方法,這個類自己就會向所有註冊過的觀察者發送訊息,訊息內容是什麼呢?內容就是m,是存放訊息的容器,觀察者可以收到這個改變了狀態的容器,然後對它進行操作,從而實現了對容器的監聽,當然,我們只實現了對add方法的監聽,你也可以試試其他的。
需要特別注意的是這是一個不完整的單例類,寫成單例是為了要保證整個jvm中只有這一個存放訊息的容器,而不寫成完整的單例,原因是將來可能要提供另外的執行個體化方法。所以理解起來可能稍微難一點,大家可以參考一下設計模式中的單例模式。
下面就是編寫觀察者並且註冊它:
/*
* Created on 2004-8-11
*/
package com.gwnet.smsMessenger.bromon;
import java.util.*;
/**
* @author Bromon
*/
public class MessageObserver implements Observer
{
/* (non-Javadoc)
* @see java.util.Observer#update(java.util.Observable, java.lang.Object)
*/
public void update(Observable arg0, Object arg1)
{
List l=(List)arg1;
Message m=(Message)l.get(l.size()-1);
String receiver=m.getReceiver();
System.out.println("給"+m.getReceiver()+”的新訊息:”+m.getContent());
}
}
這個類繼承Oberver介面,update(Observable,Object)是必須提供的方法,在這個方法中我們接收被觀察類傳過來的資料(含有訊息的容器),然後取出其中最後一個,讀取它的內容。
Java裡的觀察者使用起來是非常簡單的。我們的例子好處是所有的操作都在記憶體中進行,而且不需要輪詢,效率非常高,缺點是一旦當機記憶體中的資料就丟失了,所以如果有一套比較完善的對象緩衝機制,就可以應付複雜的應用,寫出高效簡潔的多線程伺服器。