設計模式之:解剖觀察者模式
為了便於理解,首先我舉一個現實生活中的例子:在快樂男生比賽過程其實就是觀察者的一個體現,可以這樣說吉傑是一個被觀察者,而楊二,包小柏,還有巫啟賢就是3個觀察者,被觀察者操作(唱歌)時,觀察者們就開始操作(評分),被觀察者唱歌就是通知觀察者們進行評分。
GoF說道:Observer模式的意圖是“定義物件間的一種一對多的依賴關係,當一個物件的狀態發生改變時,所有依賴於它的物件都得到通知並被自動更新”。從這段話裡我們可以得到兩個資訊,如下:
1, 觀察者(具體執行操作的物件,有多個)
2, 被觀察者(顧名思義是被觀察的物件,如果該物件發生某些變化則通知觀察者執行對應的操)
接下來我們看一下附件中的圖(請下載附件中的圖
- publicclass Observable {
- privateboolean changed = false;
- private Vector obs;
- //建立被觀察者時就建立一個它持有的觀察者列表,注意,這個列表是需要同步的。
- public Observable() {
- obs = new Vector();
- }
- /**
- * 新增觀察者到觀察者列表中去
- */
- publicsynchronizedvoid addObserver(Observer o) {
- if (o == null)
- thrownew NullPointerException();
- if (!obs.contains(o)) {
- obs.addElement(o);
- }
- }
- /**
- * 刪除一個觀察者
- */
- publicsynchronizedvoid deleteObserver(Observer o) {
- obs.removeElement(o);
- }
- /**
- * 通知操作,即被觀察者發生變化,通知對應的觀察者進行事先設定的操作,不傳引數的通知方法
- */
- publicvoid notifyObservers() {
- notifyObservers(null);
- }
- /**
- * 與上面的那個通知方法不同的是,這個方法接受一個引數,這個引數一直傳到觀察者裡,以供觀察者使用
- */
- publicvoid notifyObservers(Object arg) {
- Object[] arrLocal;
- synchronized (this) {
- if (!changed)
- return;
- arrLocal = obs.toArray();
- clearChanged();
- }
- for (int i = arrLocal.length-1; i>=0; i--)
- ((Observer)arrLocal[i]).update(this, arg);
- }
- }
public class Observable {
private boolean changed = false;
private Vector obs;
//建立被觀察者時就建立一個它持有的觀察者列表,注意,這個列表是需要同步的。
public Observable() {
obs = new Vector();
}
/**
* 新增觀察者到觀察者列表中去
*/
public synchronized void addObserver(Observer o) {
if (o == null)
throw new NullPointerException();
if (!obs.contains(o)) {
obs.addElement(o);
}
}
/**
* 刪除一個觀察者
*/
public synchronized void deleteObserver(Observer o) {
obs.removeElement(o);
}
/**
* 通知操作,即被觀察者發生變化,通知對應的觀察者進行事先設定的操作,不傳引數的通知方法
*/
public void notifyObservers() {
notifyObservers(null);
}
/**
* 與上面的那個通知方法不同的是,這個方法接受一個引數,這個引數一直傳到觀察者裡,以供觀察者使用
*/
public void notifyObservers(Object arg) {
Object[] arrLocal;
synchronized (this) {
if (!changed)
return;
arrLocal = obs.toArray();
clearChanged();
}
for (int i = arrLocal.length-1; i>=0; i--)
((Observer)arrLocal[i]).update(this, arg);
}
}
這裡只列出了一些常用的操作,大家如有不明白可以自己看java.util包下的Observable,每一個方法前都有詳細的解釋。但是以上列出的方法對於這個例子來說已經夠用了。
當我們自己的被觀察者繼承這個Observable類是,我們就自動的獲取到被觀察者的一切條件了。很方便是不是,這也是為什麼sun要把Observable放到java.util包中的原因,就是為了方便開發者。
講完了被觀察者再讓我們來看看觀察者,在上面的程式碼中有一個addObserver(Observer o)方法,由此我們可以看出來,給被觀察者新增觀察者時就是用這個方法,那麼也就是說觀察者一定是Observer的子類或者實現,我們看一下java.util.Observer吧:
- publicinterface Observer {
- /**
- * This method is called whenever the observed object is changed. An
- * application calls an <tt>Observable</tt> object's
- * <code>notifyObservers</code> method to have all the object's
- * observers notified of the change.
- *
- * @param o the observable object.
- * @param arg an argument passed to the <code>notifyObservers</code>
- * method.
- */
- void update(Observable o, Object arg);
- }
- }
public interface Observer {
/**
* This method is called whenever the observed object is changed. An
* application calls an <tt>Observable</tt> object's
* <code>notifyObservers</code> method to have all the object's
* observers notified of the change.
*
* @param o the observable object.
* @param arg an argument passed to the <code>notifyObservers</code>
* method.
*/
void update(Observable o, Object arg);
}
}
很好,這是一個介面,介面中就只有一個方法,update,方法中有兩個引數,Observable和一個object,第一個引數就是被觀察的物件,而第二個引數就得看業務需求了,需要什麼就傳進去什麼。我們自己的觀察者類必須實現這個方法,這樣在被觀察者呼叫notifyObservers操作時被觀察者所持有的所有觀察者都會執行update操作了(當然如果你override這個方法,你甚至可以指定何種情況下只執行某種observer了,是不是比較像責任鏈模式了)。
那麼到這裡我們也差不多可以把觀察者模式應用到專案中去了。首先讓我們來實現被觀察者(因為實現被觀察者非常簡單)
首先讓我們來實現一個傳送郵件的觀察者:
- /**
- * @author 張榮華(ahuaxuan)
- * @version $Id$
- */
- publicclass MailObserver implements Observer{
- /**
- * 這個類取名為MailObserver,顧名思義,她是一個用來發送郵件的觀察者
- */
- publicvoid update(Observable o, Object arg) {
- System.out.println("傳送郵件的觀察者已經被執行");
- }
- }
/**
* @author 張榮華(ahuaxuan)
* @version $Id$
*/
public class MailObserver implements Observer{
/**
* 這個類取名為MailObserver,顧名思義,她是一個用來發送郵件的觀察者
*/
public void update(Observable o, Object arg) {
System.out.println("傳送郵件的觀察者已經被執行");
}
}
接著再讓我們來實現一個傳送jms訊息的觀察者:
- /**
- * @author 張榮華(ahuaxuan)
- * @version $Id$
- */
- publicclass JMSObserver implements Observer{
- publicvoid update(Observable o, Object arg) {
- System.out.println("傳送訊息給jms伺服器的觀察者已經被執行");
- }
- }
/**
* @author 張榮華(ahuaxuan)
* @version $Id$
*/
public class JMSObserver implements Observer{
public void update(Observable o, Object arg) {
System.out.println("傳送訊息給jms伺服器的觀察者已經被執行");
}
}
如上所見,觀察者的實現完全跟業務相關。是否複雜就得看你得業務是否複雜了。
接下來讓我們再來實現被觀察者,示例如下:
- /**
- * @author 張榮華(ahuaxuan)
- * @version $Id$
- */
- publicclass Subject extends Observable{
- /**
- * 業務方法,一旦執行某個操作,則通知觀察者
- */
- publicvoid doBusiness(){
- if (true) {
- super.setChanged();
- }
- notifyObservers("現在還沒有的引數");
- }
- publicstaticvoid main(String [] args) {
- //建立一個被觀察者
- Subject subject = new Subject();
- //建立兩個觀察者
- Observer mailObserver = new MailObserver();
- Observer jmsObserver = new JMSObserver();
- //把兩個觀察者加到被觀察者列表中
- subject.addObserver(mailObserver);
- subject.addObserver(jmsObserver);
- //執行業務操作
- subject.doBusiness();
- }
- }
/**
* @author 張榮華(ahuaxuan)
* @version $Id$
*/
public class Subject extends Observable{
/**
* 業務方法,一旦執行某個操作,則通知觀察者
*/
public void doBusiness(){
if (true) {
super.setChanged();
}
notifyObservers("現在還沒有的引數");
}
public static void main(String [] args) {
//建立一個被觀察者
Subject subject = new Subject();
//建立兩個觀察者
Observer mailObserver = new MailObserver();
Observer jmsObserver = new JMSObserver();
//把兩個觀察者加到被觀察者列表中
subject.addObserver(mailObserver);
subject.addObserver(jmsObserver);
//執行業務操作
subject.doBusiness();
}
}
到此為止,我們已經簡單得實現了觀察者模式,讓我們來執行一下上面main方法,執行結果如下:
傳送訊息給jms伺服器的觀察者已經被執行
傳送郵件的觀察者已經被執行
有了jdk對觀察者模式的支援,程式設計師在實現基本的觀察者模式時應該說是易如反掌,這也從一個側面反應出觀察者模式的地位,它的地位應該是非常重要的,引用Nicholas Lesiecki(此人是上面所指的ibm那篇文章的作者)的話說,它是“設計模式中的皇后”。
通過上面的介紹和簡單的例項,現在你也可以實現一個自己的觀察者了(有時候這個簡單的模型並不滿足我們的需要,在這種情況下只好自己去寫一個類似的但是是為業務定製的observable和observer類了,但顯然這中情況並不常見)。事實上spring對觀察者模式也有很好的支援(spring可以通過配置檔案來執行addobserver方法,這樣就可以隱式的把觀察者加到被觀察者的列表中去了,前提是你的被觀察者是spring管理的一個bean之一,詳見3樓回覆,或者也可以利用afterPropertiesSet方法在被觀察者這個bean初始化完成之後立刻把需要的觀察者新增進去),除了spring,hibernate中也大量的使用了觀察者模式,同時,如果你想再深入一點,可以看看本文第二段給的ibm技術文章的連結。