1. 問題的提出 以前做一個界面的時候經常會碰到這樣的尴尬情況:希望保留各個獨立的組件(類),但又希望它們之間能夠相互通信。譬如Windows中的EXPlorer,我們希望鼠標點擊左邊是樹型目錄的一個節點,右邊的文件浏覽能及時列出該節點目錄下的文件和子目錄,類似這樣一個簡單的應用,假如只有一個類繼續JFrame,而樹型組件和浏覽文件的面板作為成員,就像:
public class MainFrame extends JFrame
{
JPanel treePanel;
JTree tree;
JPanel filePanel;
...
}
這樣當然輕易在兩者之間傳遞消息,但是可擴展性較差。通常輕易想到的是兩種辦法:在一個組件裡保留另一個組件類型的成員,初始化時作為參數傳入引用,比如:
class TreePanel extends JPanel
{
JTree tree;
...
}
class FilePanel extends JPanel
{
public FilePanel(JTree tree){...}
...
}
或者將一個組件線程化,不停地監聽另一個組件的變化,然後作出相應的反映,比如:
class TreePanel extends JPanel
{
JTree tree;
...
}
class FilePanel extends JPanel implements Runnable
{
public void run()
{
while (true)
{
//監聽tree的變化
}
...
}
...
}
這樣確實可以達到我們的目的,但是第一種方案顯然不利於松散耦合,第二種方案比較占用系統資源。通過學習設計模式,我們發現可以用Observer模式來解決這個問題。
2. Observer模式 設計模式分為創建型、結構型和行為型,其中行為型模式專門處理對象間通信,指定交互方式等,Observer模式就是屬於行為型的一種設計模式。按照“四人幫”(Gang of Four)在“Design Patterns”裡的定義,Observer模式“定義對象間的一種一對多的依靠關系,當一個對象的狀態發生改變時, 所有依靠於它的對象都得到通知並被自動更新”,這個描述正好符合我們對“組件通信”問題的需求。讓我們先看看Observer模式的結構:
其中各元素的含義如下:
Subject:被觀察的目標的抽象接口,它提供對觀察者(Observer)的注冊、注銷服務,Notify方法通知Observer目標發生改變;
Object:觀察者的抽象接口,Update方法是當得到Subject狀態變化的通知後所要采取的動作;
ConcreteSubject:Subject的具體實現;
ConcreteObserver:Observer的具體實現
Observer模式在實現MVC結構時非常有用,為數據和數據表示解耦合。
進入討論組討論。
3. Java中的Observer模式:Observer和Observable
<!-- frame contents -->
<!-- /frame contents -->
在大致了解了Observer模式的描述之後,現在我們更為關心的是它在Java中是如何應用的。幸運的是,自從JDK 1.0起,就有了專門處理這種應用的API,這就是Observer接口和Observable類,它們是屬於java.util包的一部分。看來Java的開發者們真是深谙設計模式的精髓,而Java的確是為了真正的面向對象而生的,呵呵!
這裡的Observer和Observable分別對應設計模式中的Observer和Subject,對比一下它們定義的方法,痕跡還是相當明顯的:
Observer的方法:
update(Observable subject, Object arg) 監控subject,當subject對象狀態發生變化時Observer會有什麼響應,arg是傳遞給Observable的notifyObservers方法的參數;
Observable的方法:
addObserver(Observer observer) observer向該subject注冊自己
hasChanged() 檢查該subject狀態是否發生變化