轉載一篇觀察者模式和事件處理模式的介紹

  一.引子:觀察者模式中兩個成員的對話

      <<Head First Design Pattern>> 第二章中在講到觀察者模式的時候有段很經典的,是觀察者模式中兩個角色Subject跟Observer的對話,以下稱Subject爲S,Observer爲O(不是完全按裏面翻譯的):

      S:很高興,爲了改善咱兩的關係,終於等到了今天這個機會跟Observer面對面地來交流。

      O:哦,真的啊?我還以爲您從來都沒有太在意我們這些Observers啊。

      S:呃,我一直都盡我所能做好我自己的工作啊,不是嗎?每每在關鍵的時刻我都會告訴你們究竟是發生什麼事了。。。我不認識你們中的每個人這樣不意味着我是不在意你們的啊,而且對於你們來說,有一件最重要的事情我是知道的:你們都實現了Observer這樣的接口,每當在關鍵時候,我告訴你們發生什麼事了,你們就可以馬上地去處理了。

      O:嗯,確實是這樣啊。但是那只是我們身上的一小部分啊,還有很多您是不知道的啊。無論怎樣,我對您還是相當瞭解的。。。

       S:哦,是嗎?都瞭解哪些呢,不妨說來聽聽。

       O:好的,比如說當您那邊的情況一發生了什麼變化,您始終都是把這些新“情報”第一時間地傳達到我們中的每個Observer,這樣我們就時刻都很清楚您那邊是處於什麼樣的狀態。但是有時這樣也使我們感到很苦惱啊。。。

      S:哦,對此我表示抱歉。我必須把我的狀態送給我的通知器比如說notifyObservers()然後讓它來通知你們,所以你們這些懶惰的Observers就知道發生了什麼事了。

     O:OK,等等,首先我們不是懶惰,因爲您一旦通知我們事情有什麼變化了,我們得在您Subject先生和您的通知器之間處理很多其它的事情啊;其次,爲什麼不讓我們接近您呢,這樣我們就可以很方便地拿到我們每個人真正所關心的那方面啊,而您總是把事情的方方面面都推到了我們每個人的面前,有些對我一點作用也沒有。

     S:嗯。。。我想那樣會更起作用。跟你說這樣的情況,你們Observers正在觀察XX公司的一個具有一千多個字段的僱員信息,你們想讓我在那些僱員們的某些屬性發生變化後告訴你們,那你說在這種情況下我讓你們進入我的個人世界,然後讓你得到想要的,你們知道哪些屬性發生變化嗎,不可能的!也許是僱員的薪水變化了,也許是他們換了一個老闆。這種情況下將可能是一件很危險的事情啊!所以說我不能讓你們進來我的個人世界然後隨你們探聽察看我所擁有的一切。

   O:那您爲什麼不留個公開的getter方法,從而我們就可以藉助這個getter方法得到我們自己想要的那些啊?

   S:是的,我確實可以讓你們拉(pull)到我身上的狀態。但是如果像你剛纔所說的那樣來做的話會更加方便嗎?如果每次你都必須到進入我的個人世界然後拿走你們所想要的東西,這樣的話你可能就得產生多個調用重複的getter方法來得所有你們想要的東西。這就是爲什麼我更喜歡把東西直接推(push)給你們的原因了。。。然後再個通知器裏面你們就可以直接拿到所需的任何東西了。

  O:不要做得這麼的積極啊!在我們這些Observers中,各種各樣的性格都有,你無法意料到我們需要的每件東西。就讓我們進入您的個人世界,然後拿到我們所需要的東西。像這種方式的話,假如我們當中的某些Observers只是需要某些東西這樣的話,這樣的話我們就不會被迫接受給予的全部東西了。這樣做也很方便以後地擴展啊。現在假設出現這種情況:由於各方面的原因,你壯大了自己,必須增加某種狀態,如果是使用讓我們自己拿(pull)的方式的話,您就不必爲修改我們每個Observer的update方法,然後增加那個新來的東西這樣的事情而到處奔波,您只需要在自己身上多寫一個getter方法來支持這個新進來的狀態就行了。

  S:嗯,其實我能看出來無論是“推我”還是“拉你”都各有各的優點啊。我已經注意到了Java已經有了內置的對我們觀察者模式的支持了,它可以允許你自己選擇是要推我,還是拉你。

  O:哦,真的?我想我要去感受下這種好處了。。。

  S:嗯,好極了。。。我想我也要去看一個好的“拉你”的例子了,從而改變我的心態啊。。。

  O:什麼?我沒聽錯吧,難道咱倆已經達到共識了?我想希望總是有的啊。。。

 (完)

二 究竟是要“推我”還是“拉你”?

   聽完觀察者模式兩個成員的經典對話之後,我想大家對觀察者模式會有了更深刻的印象。究竟是要“推我”還是“拉你”,從上面的對話我們很容易下這樣結論:完全取決於被觀察者對象的複雜性,如果被觀察對象比較複雜,並且觀察者需要有一個提示,那麼推模型是合適的。如果被觀察的對象比較簡單,那麼拉模型就很合適。

觀察者模式Java編程中的應用(重點學習JavaBeans事件模型)

      學習設計模式的過程中,一直都有這種感受,看JDK的API甚至是源代碼,你會發現比已經更有條理性了,更好理解了,可以說要想研究JDK源代碼(我想別的源代碼也一樣)必須很好地理解了設計模式。有兩種方式來查看觀察者模式的細節,第一種辦法是瞭解定義在java.utilObserverObservable類。第二種方法是研究註冊事件監聽器的JavaBeans組件模型。在創建JavaBeans事件模型之前,ObserverObservable類就描述了觀察者模式的實現。換句話說,自從Java平臺1.0版本,這些類就已經存在了。由於這些類在設計上並沒有什麼技術錯誤,因此一直沿用下來了。這些類現在還可以用於實現觀察者模式,但是用的更多的是JavaBeans事件模型,我也想花多點時間研究下在這方面的應用,這會對我們在GUI編程方面上有更多的幫助。

    較喜歡這樣的劃分事件處理模型中的對象:事件對象,事件製造者對象和事件接收者對象。其中,某一個對象是事件的製造者,其餘對象是事件的接收者;而事件對象本身則裝了有關事件的信息。當事件製造者的內部狀態發生了變化時,會根據需要創建一個代表其狀變化的事件對象,並將它傳給所有登記過的事件接收者對象。對於這種機制,我想大家在聽了上面的對話後應該可以很自然地想到這就是建立在觀察者模式的基礎之上的。其實,從網上資料得知Java1.0的事件處理機制是建立在責任鏈模式的基礎上的(呵呵,寫這篇文章的時候還沒有學到這種設計模式呢~~)在學習這種事件機制之前,我們先來看看MVC中的Model-View,它們作爲MVC模式的一部分,就是Subject和Obverser的關係,因而,模型的改變必須要在UI對象中體現出來。Swing使用了JavaBeans的事件模型來實現這種通知機制。根據前面Subject和Obverserr的對話,有兩種實現辦法,一是僅僅通知事件監聽者狀態改變了,然後由事件監聽者向模型提取必要的狀態信息。這種機制對於事件頻繁的組件很有效。另外的一種辦法是模型向監聽者發送包含了已改變的狀態信息的通知給UI。這兩種方法根據其優劣被分別是現在不同的組件中。比如在JScollBar中使用的是第一種方法,在JTable中使用的是第二種方法。順便提下JTable,我們在基於C/S架構的應用中如果要從數據庫中取出一批數據然後把它大列表中顯示,這時候會用到大量的JTable,到時候我們就可以更好地理解這種機制了。

 

   回到事件監聽機制,對Model而言,爲了能夠支持多個View,它並不知道具體的每一個View。它維護一個對其數據感興趣的Obverser的列表。調用addXXXListener()方法增加一個監聽器,調用removeXXXListener()方法刪除一個監聽器,這兩個方法都要需要一個相應類型的監聽器類型。所有的AWT組件都是java.awt.Component的子類,它們都從Component類繼承了各個addXXXListener()方法。

  舉個小例子:

  ActionListener listener = new ActionListener() {

public void actionPerformed(ActionEvent actionEvent) {

   System.out.println("監聽器收到通知!");

  }

};

  JButton button = new JButton("單擊我!");

  button.addActionListener(listener);

   在這個例子中,button包括了維護一個監聽器聚集類,並提供各種管理監聽器對象聚集的方法,比如addActionListener等,可以看出上面的listener就是一個監聽器對象它實現了ActionListener接口的actionPerformed方法,這時如果用戶單擊了button,就會激發actionPerformed方法,其實這裏的actionPerformed就相當於Observer中的update()方法。

   在這裏,我們實現一個監聽器接口,將其附加到監聽的對象上。監聽的對象就是被監聽的。它的職責是記住誰在監聽,在JavaBeans組件模型中,用於附加和解除Observer對象的接口是添加和刪除監聽器命名模式。當監聽對象的狀態改變的時候,它會通知Observer對象。這種設計模式的一個主要目標就是將對象和觀察者的耦合度降低。當JButton被選擇的時候,並非調用一個叫做ButtonNotification類的特定方法,通知動作被抽象到一個接口中,任何類都可以實現它。JButton並不關心綁定的監聽器是什麼。事實上,按鈕不關心實現類是不是被修改了。它關心的是觀察者實現了這個監聽器。

   下面這段摘自網上的資料,和大家分享:

    在使用觀察者模式的時候,有很多的複雜問題需要注意。首先是可能出現內存泄露。監聽的對象維護着一個觀察者的引用。在監聽對象釋放這個引用之前,垃圾收集器都不能刪除觀察者。一定要清醒的認識到這種可能,在合適的時候刪除掉觀察者。另外需要注意的是觀察者對象維持在一個無序的集合之中。至少當註冊監聽器的時候是這樣。你沒有必要知道先被註冊的監聽器是先被調用還是後被調用。如果你需要有序的調用,例如A必須先被調用然後是B。那麼你必須引入一箇中間層的對象來強制這種順序。簡單的按照順序來註冊監聽器是不能確保調用順序的。

   四 總結

  觀察者模式是設計模式中的應用相當廣泛的一種,希望本文能給大家學習觀察者模式起到拋磚引玉的作用。

發佈了38 篇原創文章 · 獲贊 1 · 訪問量 5630
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章