在這一部分的內容中,介紹的是一個相對簡單但功能強大的模式:Observer模式。希望通 過這部分地敘述,大家看了之後,能夠對設計模式有一個比較全面地,感性的認識。
很多時候,對於一個設計來說(軟件上的,建築上的,或者它他工業上的),經驗是至關 重要的。好的經驗給我們以指導,並節約我們的時間;壞的經驗則給我們以借鑒,可以減少 失敗的風險。然而,從知識層面上來講,經驗只是作為一種工作的積累而存在於個人的大腦 中的,很難被傳授或者記錄。為了解決這樣的問題,人們提出了所謂的模式的概念。所謂模 式,是指在一個特定背景下,反復出現的問題解決方案。模式是經驗的文檔化。
軟件模式的概念現在比較的廣泛,涉及到分析,設計,體系結構,編碼,測試,重構等軟 件構造生命期中的各個部分。這兒主要討論的是設計模式,指的是在軟件設計過程中反復出 現的一些問題的解決方法了。不過我們一般在提到設計模式的時候,一般都是指GOF的經典書 《Design Pattern--Elements of Reusable Object-Oriented Software》出現的23個模式, 因而,它是具體的針對於面向對象軟件設計過程的。
從全局上看來,模式代表了一種語言,一種被文檔化的經驗,甚至是一種文化。往往很多 不方便描敘,或者描敘起來很復雜的問題,用模式語言來敘說,會讓聽者產生心領神會的感 覺。當然,這需要交流雙方都能夠很好地把握模式語言的含義。然而,這並不是一件容易的 事情。模式在各個人的理解上往往存在差異,這篇文章旨在從一個具體的應用角度:Java類 庫,來闡敘設計模式。並結合具體的例子,希望能夠加深大家對設計模式的理解。
這兒說的Java類庫,其實並沒有局限於JDK本身,還包括了一些其他的類庫中的例子,比 如JAXP等(當然,下一個版本的JDK中也會包含JAXP了)。其實設計模式的思想現在應用的如 此廣泛,無論在什麼樣的設計中,只要稍微大一點的設計,都可以找到很多很多設計模式的 蹤跡,或者說都不可避免的用到設計模式。下面所講的設計模式,大部分都是GOF的那部經典 中出現過的23個模式,然而,還有一些,比如MVC,並不屬於那裡。一般的來講,我們認為 GOF的23個模式是一些中級的模式,在它下面還可以抽象出一些更為一般的低層的模式,在其 上也可以通過組合來得到一些高級的模式。當然,這兒的低中高的區別,如同區別不同的語 言一樣,並沒有優劣之分,僅僅是在應用層面上的區別。
Observer模式
Observer模式的功用,是希望兩個(或多個)對象,我們稱之為Subject和Observer,當 一方的狀態發生改變的時候,另一方能夠得到通知。也就是說,作為Observer的一方,能夠 監視到Subject的某個特定的狀態變化,並為之做出反應。一個簡單的例子就是:當一個用戶 視圖中的數據被用戶改變後,後端的數據庫能夠得到更新,而當數據庫被其他方式更新後, 用戶視圖中的數據顯示也會隨之改變。
圖一:Obverser模式的類圖
在JDK中實際上有一個對Observer模式的簡單的實現:就是類java.util.Observerable和 接口java.util.Observer。java.util.Observerable類對應於Subject,而 java.util.Observer就是觀察者了。JDK中並沒有把這兩個部分都設計為接口,而是讓類 java.util.Observerable提供了部分的實現,簡化了許多編程的工作。當然,這也減少了一 定的靈活性。
下面列出了Observer和Observeral的函數列表,及其簡單的功能說明
java.util.Observer:
public void update(Observable obs, Object obj)
java.util.Observer 接口很簡單,只定義了這一個方法,狹義的按照Observer模式的說 法,Observer應該在這個方法中調用Subject的getXXX()方法來取得最新的狀態,而實際上, 你可以只是在其中對Subject的某些事件進行響應。這便是Java中的代理事件模型的一個雛形 --對事件進行響應。只不過,在Observer模式中將事件特定化為某個狀態/數據的改變了。
java.util.Observable
public void addObserver(Observer obs)
向Subject注冊一個Observer。也就是把這個Observer對象添加到了一個 java.util.Observable內部的列表中。在JDK中對於這個列表是簡單的通過一個 java.util.Vector類來實現的,而實際上,在一些復雜的Observer模式的應用中,需要把這 個部分單另出來形成一個Manager類,來管理Subject和Observer之間的映射。這樣,Subject 和Observer進一步的被解藕,程序也會具有更大的靈活性。
public void deleteObserver(Observer obs)
從Subject中刪除一個已注冊了Observer的引用。
public void deleteObservers()
從Subjec中刪除所有注冊的Observer的引用。
public int countObservers()
返回注冊在Subject中的Observer個數。
protected void setChanged()
設置一個內部的標志以指明這個Ovserver的狀態已經發生改變。注意這是一個protected 方法,也就是說只能在Observer類和其子類中被調用,而在其它的類中是看不到這個方法的 。
protected void clearChanged()
清除上敘的內部標志。它在notifyObservers()方法內部被自動的調用,以指明Subject的 狀態的改變已經傳遞到Ovserver中了。
public boolean hasChanged()
確定Subject的狀態是否發生了改變。
public void notifyObservers(Object obj)
它首先檢查那個內部的標志,以判斷狀態是否改變,如果是的話,它會調用注冊在 Subject中的每個Observer的update()方法。在JDK中這個方法內部是作為synchronized來實 現的,也就是如果發生多個線程同時爭用一個java.util.Observerable的notifyObservers() 方法的話,他們必須按調度的等待著順序執行。在某些特殊的情況下,這會有一些潛在的問 題:可能在等待的過程中,一個剛剛被加入的Observer會被遺漏沒有被通知到,而一個剛剛 被刪除了的Observer會仍然收到它已經不想要了的通知。
public void notifyObservers()
等價於調用了notifyObservers(null)。
因而在Java中應用Observer就很簡單了,需要做的是:讓需要被觀察的Subject對象繼承 java.util.Observerable,讓需要觀察的對象實現java.util.Observer接口,然後用 java.util.Observerable的addObserver(Observer obj)方法把Observer注冊到Subject對象 中。這已經完成了大部分的工作了。然後調用java.util.Observerable的notifyObservers (Object arg)等方法,就可以實現Observer模式的機理。我們來看一個簡單使用了這個模式 的例子。這個例子有三個類:FrameSubject,DateSubject,FrameObject和EntryClass, FrameSubject中用戶可以設置被觀察的值,然後自動的會在FrameObject中顯示出來, DateSubject封裝被觀察的值,並且充當Observer模式中的Subject。
public class FrameSubject extends JFrame {
…………..
//因為無法使用多重繼承,這兒就只能使用對象組合的方式來引入一個
//java.util.Observerable對象了。
DateSubject subject=new DateSubject();
//這個方法轉發添加Observer消息到DateSubject。
public void registerObserver(java.util.Observer o){
subject.addObserver(o);
}
//數據改變,事件被觸發後調用notifyObservers()來通知Observer。
void jButton1_actionPerformed(ActionEvent e) {
subject.setWidthInfo(Integer.parseInt(jTextField1.getText()));
subject.setHeightInfo(Integer.parseInt(jTextField2.getText()));
subject.notifyObservers();
}
……………
}
public class DateSubject extends Observable {
//封裝被觀察的數據
private int widthInfo;
private int heightInfo;
public int getWidthInfo() {
return widthInfo;
}
public void setWidthInfo(int widthInfo) {
this.widthInfo = widthInfo;
//數據改變後,setChanged()必須被調用,否則notifyObservers()方法會不起作用
this.setChanged();
}
public void setHeightInfo(int heightInfo) {
this.heightInfo = heightInfo;
this.setChanged();
}
public int getHeightInfo() {
return heightInfo;
}
}
public class FrameObserver extends JFrame implements java.util.Observer {
…………..
//觀察的數據
int widthInfo=0;
int heightInfo=0;
//在update()方法中實現對數據的更新和其它必要的反應。
public void update(Observable o, Object arg) {
DateSubject subject=(DateSubject) o;
widthInfo=subject.getWidthInfo();
heightInfo=subject.getHeightInfo();
jLabel1.setText("The heightInfo from subject is: ");
jLabel3.setText(String.valueOf(heightInfo));
jLabel2.setText("The widthInfo from subject is: ");
jLabel4.setText(String.valueOf(widthInfo));
}
…………….
}
public class EntryClass {
public static void main(String[] args) {
……………..
FrameSubject frame = new FrameSubject();
FrameObserver frame2=new FrameObserver();
//在Subject中注冊Observer,將兩者聯系在一起
frame.registerObserver(frame2);
…………..
frame.setVisible(true);
frame2.setVisible(true);
……………..
}
}
我認為在JDK中這個Observer模式的實現,對於一般的Observer模式的應用,已經是非常 的足夠了的。但是一方面它用一個類來實現了Subject,另一方面它使用Vector來保存 Subject對於Observer的引用,這雖然簡化了編程的過程,但會限制它在一些需要更為靈活, 復雜的設計中的應用,有時候(雖然這種情況不多),我們還不得不重新編寫新的Subject對 象和額外的Manager對象來實現更為復雜的Observer模式的應用。
小結:
這一部分主要的討論了模式的概念。隨著現代軟件工業的不斷進步,軟件系統的規模的日 益擴大,越來越需要對某些個不斷出現的問題進行模式化思維,以成功的經驗或者失敗的教 訓來減少軟件開發失敗的風險。模式代表了一種文檔化的經驗,它為某一類的問題提供了最 好(或者說很好)的解決方案,使得即使不是經驗豐富的軟件工程師,也能夠根據模式來構 建相對成功的系統。本節給出的一個Obverser模式的示例,比較好的說明了這一點。 Obverser模式主要解決在對象間的狀態映射或者鏡像的問題。