1. 程式人生 > >觀察者模式( Observer Pattern ): MVC的進一步泛化

觀察者模式( Observer Pattern ): MVC的進一步泛化

參考書籍:

  1. 《Design Patterns: Elements of Reusable Object-Oriented Software》

在商業專案中第一次讀到觀察者模式的程式碼是一個非同步伺服器訊息分發的實現部分。由此開始學習了一下 。

設計模式用前須知

  • 設計模式中一句出現頻率非常高的話是,“ 在不改動。。。。的情況下, 實現。。。。的擴充套件“ 。
  • 對於設計模式的學習者來說,充分思考這句話其實非常重要, 因為這句往往只對框架/ 工具包的設計才有真正的意義。因為框架和工具包存在的意義,就是為了讓其他的程式設計師予以利用, 進行功能的擴充套件,而這種功能的擴充套件必須以不需要改動框架和工具包中程式碼為前提
  • 對於應用程式的編寫者, 從理論上來說, 所有的應用層級程式碼至少都是處於可編輯範圍內的, 如果不細加考量, 就盲目使用較為複雜的設計模式, 反而會得不償失, 畢竟靈活性的獲得, 也是有代價的。

觀察者模式與MVC

如果說單例模式工廠方法模式是入門級程式設計師都必須掌握的方法, 那麼觀察者模式則是所有實際專案開發者都會直接或者間接用到的模式, 原因是:

  • 觀察者模式是MVC模式的一種更為泛化的描述(引自參考書籍1)

    • MVC 模式中的Model 其實就是觀察者模式中的被觀察者(Subject), Model的狀態變化, 都會引發View 層次的變動,而MVC的View層次自然就對應觀察者模式中的觀察者(Observer)
  • 觀察者模式顧名思義, 主要有兩種角色:

    • 觀察者(Observer)
    • 觀察物件/被觀察者(Subject/ Observable)

觀察者模式結構圖--引自參考書籍一

  • 圖例說明

    • 圖片中的空心三角箭頭,代表著繼承(extends)或實現(Implement)關係, 由繼承者/實現者 指向 被繼承者/被繼承者。
    • 圖片中的實心三角箭頭且箭頭末尾沒有圓圈的, 代表著單一的引用關係, 但是被引用的物件也有可能被其他物件引用。
    • 圖片中的實心三角箭頭且箭頭末尾有圓圈的, 代表著一對多的引用關係。
    • 圖片中的末端有圓圈的虛線是一個對方法體內容用虛擬碼說明的關係
  • 上面的結構圖省略了一個對初學者而言很重要的資訊,觀察者與觀察物件的關聯是何時建立的。

    • 注意到觀察物件(Subject)定義了Attach(Observer), Detach(Observer) 方法, 但這裡隱含的一個問題是,是觀察者來呼叫Attach/Detach 方法, 還是被觀察者呼叫 Attach Detach 方法 ?
      • 稍微思考一下可以得出答案, 是否觀察某一個物件應該有觀察者來決定, 而不是被觀察者決定, 所以Attach/Detach 方法應該由Observer 來呼叫。
    • 那麼緊接著的第二個問題是圖例中Observer 並沒有 Subject 的引用, 如何呼叫其Attach/Detach 方法
      • 答案一: Subject 是具有全域性可訪問性(例如public class 的 public static 成員)
      • 答案二: 在Observer 被構造的時候傳入Subject 物件, 自此之後, Observer 一直持有Subject的引用。
  • 觀察者模式在下列情形時應當被使用:

    • 當對一個物件的的改動會引發其他物件的變動時,而且你無法預測有多少個物件需要被改動。
    • 當一個物件需要有能力通知其他物件, 且不需要了解這些物件是什麼型別時。
  • 觀察者模式在實現時,需要考慮的一些問題:

    1. 被觀察者與觀察者對應關係的維護方式
      • 最簡單也是最常見的方法是由被觀察者儲存需要通知的觀察者 的引用, 然後像之前的圖例中的虛擬碼所展示的一樣, 通過一個For迴圈來觸發對應觀察者的Update()方法。
        • 但這種方式的缺點是, 當觀察者比較多,而被觀察者比較少時,大量的觀察者引用可能會被重複儲存,引發比較大的儲存開銷。
      • 另一種可選的方式是關聯查詢, 如利用 HashTable 維護觀察者與被觀察者的對應關係
      • 這種方式避免了引用關係重複儲存的缺點
      • 缺點是增加了被觀察者訪問觀察者模式的開銷(需要先查HashTable,再訪問)
    2. 當一個觀察者需要觀察不止一個被觀察者時,當收到一個通知時, 如何知道是哪一個被觀察者發來了通知。
      • 這種情況,可以簡單的通過在Update()方法中, 把被觀察者的引用作為引數傳入,以此讓觀察者知道是哪一個被觀察者呼叫了Notify()方法。
    3. 何時呼叫Notify方法()
      • 第一個選擇是把Notify 動作嵌入到 被觀察者實現類的SetState方法中, 這樣保證了每一次被觀察者狀態的改變都會被通知給所有的觀察者
        • 缺點是一個操作有可能分為很多步, 其中有多次呼叫SetState方法,而其實只有最後一次SetState完成時才需要通知其他觀察者, 重複地呼叫SetState 而引發的多次Update可能會比較低效。
      • 另一種選擇是由修改被觀察者狀態的物件來決定呼叫Notify 方法的時刻
        • 好處是避免了在一些中間步驟中觸發Update方法, 缺點是把呼叫Notify方法的任務交給了被觀察者的改動方, 他們有可能忘記呼叫Notify方法。
    4. 當一個被觀察者被刪除時, 應該對相應的觀察者作何處理。
      • 刪除一個被觀察者時, 不應該導致對該觀察者無效的引用產生, 比較何時的作法時, 當一個被觀察者刪除時, 需要通知所有觀察者, 以期觀察者做出何時的處理。
    5. 如何保證觀察者物件在呼叫Notify方法之前,自身的狀態處於一個正常的狀態,而不是修改到一半的狀態。
      • 這個問題乍一看有些奇怪且不必要,但其實在被觀察者存在繼承關係時,很容易出現。 下面通過Java程式碼的例子進行說明。
// 被觀察者基類
public class BaseSubject {
    private int value = 0;
    public void operation(int newValue)
    {
        value = 1;
        notifyObservers()
    }
    public void notify()
    {
        ...
    }
}

//被觀察者子類
public class MySubject extends BaseSubject {
    private int myValue = 2;
    public void myOperation( int newValue)
    {
        super.operation(newValue); // 這個操作已經觸發了notify 方法
        this.myValue += newValue;
        .... // 後續的對被觀察者子物件的狀態修改尚未被通知給觀察者,除非最後顯示呼叫了notifyObservers() 方法

    }
}

這個問題可以通過設計模式之模板方法(Template Method)模式避免。 具體方案是定義一個元操作供子類重寫, 然後在模板方法中將 notify () 操作放在模板方法中最後進行的操作,以此來保證當觀察者子類重寫觀察者基類時, 不會遺漏Notify 操作。

  • 觀察者模式在實現時,需要考慮的一些問題(高階篇)
    1. 觀察者模式在實現時,被觀察者的狀態發生變化時, 除了要通知觀察者他們觀察的物件已經發生變化, 往往還需要向觀察者發一些額外的資訊, 這裡有兩種選擇
      • 推模型 (push model )
        • 被觀察者會把儘可能多的資訊包含在Update 方法中, 儘管被通知的觀察者可能並不需要其中的某些資訊。
          • 推模型的缺點在於默認了被觀察者瞭解哪些是觀察者所需要的內容, 這有可能使得被觀察者的重用性降低, 因為如果有新的資訊要新增到Update方法中時, 或有內容要從Update方法中刪除時,都會很不方便,因為有很多依賴於該方法的觀察者。
      • 拉模型
        • 被觀察者會在Update方法中包含最少的資訊, 如果觀察者還需要額外的資訊, 可以自行通過別的方法來獲取。
          • 拉模型的好處在於預設觀察者不瞭解觀察者所需要的資訊, 因此特定類別的觀察者需要增加獲取的資訊時, 只需要修改特定的獲取資訊的方法即可。
    2. 不同觀察者對於同一個被觀察者的狀態改變的事件類別是不一樣的, 可能A類觀察者只關心A類別事件導致的狀態變化, B類觀察者只關心B類別事件導致的狀態變化, 那麼如何使得被觀察者對於狀態的變化進行分類別通知
      • 可以修改觀察者註冊介面(Attach) 方法為void attach(Observer o, Aspect interest); 使得觀察者在註冊時,同時告訴被觀察者他關心的事件類別。 而被觀察者在特定的事件發生,可以僅僅通知那些註冊了該類別事件的觀察者。
    3. 當一個觀察者(Observer)需要觀察多個被觀察者(Subject)時 , 一個發生在兩個或多個被觀察者的改變有可能引發冗餘的Update() 方法, 此時有可能會需要引入一個ChangeManager, 該ChangeManager職責如下:
      • 維護觀察者到被觀察者的對映關係, 這樣觀察者和被觀察者都無須再保留相互之間的引用。
      • 定義特定的Update 策略, 當一個被觀察者發出請求時, 只更新對該被觀察者有依賴的觀察者。

引自參考書籍一

相關推薦

觀察模式( Observer Pattern ) MVC進一步泛化

參考書籍: 《Design Patterns: Elements of Reusable Object-Oriented Software》 在商業專案中第一次讀到觀察者模式的程式碼是一個非同

觀察模式(Observer Pattern)(二)HeadFirst中的氣象站的實現

att dex mov min first return 狀態 size sdi 1 觀察者模式的原理,首先由一個主題,當主題發送變化的時候,通知該主題的訂閱者 按照上面的分析我們來進行設計 1.抽象主題Subject public interface Subject {

設計模式九: 觀察模式(Observer Pattern)

簡介 觀察者屬於行為型模式的一種, 又叫釋出-訂閱模式. 如果一個物件的狀態發生改變,依賴他的物件都將發生變化, 那麼這種情況就適合使用觀察者模式. 它包含兩個術語,主題(Subject),觀察者(Observer), 主題管理一個觀察者的列表, 並在狀態發生變化時通知到他們. 實現層面上, 主題定義了一個觀

設計模式----行為型模式觀察模式(Observer Pattern)

/** * 天氣主題(可觀察者/目標) * @author mjs * @version 1.0.0 * @filename WeatherData.java * @time 2017-3-9 下午8:26:48 * @copyright(C) 2017 **********有限公司 */ pa

觀察模式(Observer Pattern)

是什麼? 怎麼用? 什麼情況下用? 例項!     觀察者模式:   一個目標物件管理所有依賴於它的觀察者物件,並且當它本身的狀態改變時主動發出通知。   這時候就有問題了,目標怎麼知道誰是觀察者?誰不是觀察者?而且目標要怎麼通知觀察者?   (個人感覺兩個類通訊

面向物件設計模式之---觀察模式(Observer Pattern)

今天更新的設計模式是觀察者模式。剛才稍微去看了一眼Web前端開發,很炫!很炫! 對於這個觀察者模式,我就直接開門見山地直接放出定義和UML類圖,後面再解釋。 觀察者模式的定義如下: 觀察者模式定義了一種一對多的依賴關係,讓多個觀察者物件同時監聽一個主題物件。這個主題物

Java設計模式觀察模式(Observer Pattern)

Observer Pattern 是一種常用的設計模式,它是一種事件監聽模型。該模式有兩個角色,一個是Subject, 另一個是Observer。Subject 儲存有多個Observer的引用,一旦特定的事件發生,Subject會通知它所有的Observer,Observ

設計模式觀察模式--Observer

sub 系統調用 特點 xiaomi ttr root comm wrap 切換 一、什麽是觀察者模式 1、生活中的觀察者模式 1、警察抓小偷 在現實生活中,警察抓小偷是一個典型的觀察者模式「這以一個慣犯在街道逛街然後被抓為例子」,這裏小偷就是被觀察者,各個幹

觀察模式的應用模擬MVC架構的實現

mvc架構是安卓的經典架構模式,它是觀察者模式的一個典型應用場景。今天我就用java程式來簡單模擬mvc架構的實現。 首先,簡單說下什麼是mvc架構。 mvc架構由model,view和controller三者組成,基於職責分離的原則,三者分別承擔不同的職責

Java設計模式觀察模式(Observer)

觀察者模式定義了物件間的一種一對多依賴關係,使得每當一個物件改變狀態,則所有依賴於它的物件都會得到通知並被自動更新。 它將觀察者和被觀察者的物件分離開。提高了應用程式的可維護性和重用性。 實現觀察者模式有很多形式,一種是“註冊---通知---撤銷註冊”的形式。 觀察者Observer:所有潛在的觀察

觀察模式實際應用監聽線程,意外退出線程後自動重啟

lee text 實時 之間 最終 ren tap instance and 摘要:  觀察者模式,定義對象之間的一種一對多的依賴關系,當對象的狀態發生改變時,所有依賴於它的對象都得到通知並且被自動更新。觀察者模式在JDK中有現成的實現,java.util.Obsera

Java 設計模式系列(十六)觀察模式(Observer)

for out 其中 如果 observer 業務 ets 同時 hang Java 設計模式系列(十六)觀察者模式(Observer) 觀察者模式是對象的行為模式,又叫發布-訂閱(Publish/Subscribe)模式、模型-視圖(Model/View)模式、源-監聽

【編程思想】【設計模式】【行為模式Behavioral】觀察模式Observer

setting notify tput pes env observer 設計模式 mod pre Python轉載版 https://github.com/faif/python-patterns/blob/master/behavioral/observer.py

設計模式 ( 十五 ) 觀察模式Observer(物件行為型)

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

Java原始碼分析——java.util工具包解析(五)——UUID、Base64、內建觀察模式Observer介面、EventListener、RandomAccess

UUID     關於UUID,我們需要知道它最重要的一點,就是它會生成全地球唯一的一個id,它可以作為資料庫的主鍵存在,標識各個元組。 UUID保證對在同一時空中的所有機器都是唯一的,利用機器的當前日期和時間、時鐘序列、全域性唯一的IEEE機

23種設計模式之---觀察模式(Observer Factory)

1.觀察者模式(行為型模式) 觀察者模式定義了一種一對多的依賴關係,讓多個觀察者物件同時監聽某一個主題物件。這個主題物件在狀態發生變化時,會通知所有的觀察者物件,使它們能夠自己更新自己。 2.觀察者模式結構圖 3.觀察者模式結構圖分析 Subject類,可理解為主題或抽象通知者

淺談Java設計模式——觀察模式(Observer)

一、概述         定義物件間的一種一對多的依賴關係,當一個物件的狀態發生改變時,所有依賴於它的物件都得到通知並被自動更新。觀察者模式定義了物件之間的一對多依賴關係,這樣一來,當一個物件改變狀態時,它的所有依賴者都會收到通知並且自動更新。在這裡,發生改變的物件稱之為觀

Android之觀察/被觀察模式Observer/Observable

Android之觀察者Observer初探 知識點: 1、Android觀察者模式的簡介; 2、Observer和Observable的使用例項; 3、(abstract)抽象類和抽象方法的使用; 4、新名詞記錄 { abstract:抽象關鍵詞 } 最近一直在看著觀察

設計模式 ( 十六 ) 觀察模式Observer(物件行為型)

1.概述 一些面向物件的程式設計方式,提供了一種構建物件間複雜網路互連的能力。當物件們連線在一起時,它們就可以相互提供服務和資訊。 通常來說,當某個物件的狀態發生改變時,你仍然需要物件之間能互相通訊。但是出於各種原因,你也許並不願意因為程式碼環境的改變而對程式碼做大

Java觀察模式 : Observer / Observable

/** Java觀察者模式的場景:一個女孩洗澡,被很多男孩偷看。 女孩洞察後,搜尋壞男孩,然後繼續洗澡。 三個類:Boy,Girl還有主類MainClass。 */ /* 男孩 Boy.java */ import java.util.Observable; impor