軟體設計模式學習(二十三)觀察者模式

来源:https://www.cnblogs.com/Yee-Q/archive/2020/06/01/13026151.html
-Advertisement-
Play Games

觀察者模式是一種經常使用的設計模式,在軟體系統中對象並不是孤立存在的,一個對象行為的改變可能會導致其他與之存在依賴關係的對象行為發生改變,觀察者模式用於描述對象之間的依賴關係。 模式動機 很多情況下,對象不是孤立存在的,想象這麼一個場景:你和女朋友去旅行,晚上回到賓館,女朋友穿著厚厚的大衣,從外表看 ...



觀察者模式是一種經常使用的設計模式,在軟體系統中對象並不是孤立存在的,一個對象行為的改變可能會導致其他與之存在依賴關係的對象行為發生改變,觀察者模式用於描述對象之間的依賴關係。


模式動機

很多情況下,對象不是孤立存在的,想象這麼一個場景:你和女朋友去旅行,晚上回到賓館,女朋友穿著厚厚的大衣,從外表看上去就是個臃腫的包子。你沒有反應,等到女朋友洗完澡裹著浴巾出來以後,你立馬眼睛都瞪直了,活脫脫一個大色狼(不...不要盯著人家看了啦)

從例子中,我們不難分離出兩類角色,一類為觀察者,如色眯眯的你,另一類就是被觀察者所觀察的目標,如性感的女朋友。如果觀察目標發生某個動作,觀察者就會有響應。

建立一種對象與對象之間的依賴關係,一個對象發生改變時會自動通知其他對象,其他對象將相應做出反應。發生改變的對象稱為觀察目標,被通知的對象稱為觀察者,一個觀察目標可以對應多個觀察者,而且觀察者之間沒有相互聯繫,可以根據需要增加和刪除觀察者,這就是觀察者模式的模式動機。


模式定義

定義對象間的一種一對多依賴關係,使得每當一個對象狀態發生改變時,其相關依賴對象皆得到通知並自動更新。觀察者模式又叫發佈-訂閱模式(Publish/Subscribe)模式、模型-視圖(Model/View)模式、源-監聽器(Source/Listener)或從屬者模式(Dependents)模式。觀察者模式是一種對象行為型模式。


模式分析

觀察者模式描述瞭如何建立對象與對象之間的依賴關係,那麼具體如何構造呢?

這一模式的關鍵對象是觀察目標和觀察者,一個目標可以有任意多個與之相依賴的觀察者,一旦目標狀態發生變化,所有觀察者都將得到通知。

作為對這個通知的響應,每個觀察者都將即時更新自己的狀態,與目標狀態同步,這種交互也稱發佈-訂閱(publish-subscribe)。

目標是通知的發佈者,它發出通知時不需要知道誰是它的觀察者,可以有任意數目的觀察訂閱並接收通知。

由此看一下根據該模式得出的類圖

抽象目標 Subject,典型代碼如下

import java.util.*;
public abstract class Subject {
    
    // 定義一個集合存儲任意數量的觀察者對象
    protected ArrayList<Observer> observers = new ArrayList<Observer>();
    
    // 增加一個觀察者
    public abstract void attach(Observer observer);
    // 刪除一個觀察者
    public abstract void detach(Observer observer);
    // 通知各個觀察者並調用它們的 update() 方法
    public abstract void notifyObservers();
}

具體目標類 ConcreteSubject 是實現 Subject 的一個具體子類,典型代碼如下

public class ConcreteSubject extends Subject {
    
    // 向集合中添加一個觀察者
    public void attach(Observer observer) {
        observers.add(observer);
    }
    // 從集合中刪除一個觀察者
    public void detach(Observer observer) {
        observers.remove(observer);
    }
    // 迴圈調用集合中觀察者的 updates() 方法
    public void notifyObservers() {
        for (Observer obs : observers) {
			obs.update();
        }
    }
}

也可以把 attach() 和 detach() 方法的實現放在 Subject 中,這樣就無需每個子類都去實現一次了(如果沒有特別需求的話)。另外,它還可以實現在目標類中定義的抽象業務邏輯方法(如果有的話)

抽象觀察者一般定義為一個介面,其中聲明 update() 方法,這個方法在其子類中實現,不同的觀察者具有不同的更新響應方法,典型代碼如下

public interface Observer {
	public void update();
}

具體觀察者 ConcreteObserver 中實現 update() 方法,其典型代碼如下

public class ConcreteObserver implements Observer {
    public void update() {
        //	具體更新代碼
    }
}

在使用時,客戶端首先創建具體目標對象以及具體觀察者對象(也可以使用配置文件),然後,調用目標對象的 attach() 方法,將這個觀察者對象在目標對象中登記,也就是將它加入到目標對象的觀察者集合中

Subject subject = new ConcreteSubject();
Observer observer = new ConcreteObserver();
subject.attach(observer);
subject.notifyObservers();

客戶端調用目標對象的 notifyObservers() 方法時,將調用在其觀察者集合中註冊的觀察者對象的 update() 方法,實現狀態的更新

當然,在有些複雜的情況下,具體觀察者類 ConcreteObserver 的 update() 方法在執行時,需要使用到 ConcreteSubject 中的狀態(屬性)

舉個例子,我們經常會用可視化的圖表(如柱狀圖、餅狀圖)來顯示數據,同樣的數據可能有不同的圖表顯示方法(即不同的具體觀察者),如果數據發生改變,圖標也應該實時做出改變,那自然的,圖表在更新時肯定需要用到新的數據吧

如果像上述而言,那麼 ConcreteObserver 和 ConcreteSubject 之間還存在關聯關係,在 ConcreteObserver 中定義一個 ConcreteSubject 實例,通過該實例獲取存儲在 ConcreteSubject 中的狀態屬性。但這樣一來,系統的擴展性將受到一定影響,增加新的具體目標類有時候需要修改原有觀察者的代碼,在一定程度上違反了開閉原則,但如果原有觀察者無須關聯新增具體目標,則系統擴展性不受影響

// 具體目標類
public class ConcreteSubject extends Subject {
    
    // 方便起見,attahch() 等抽象方法在抽象層做了實現了
    
    // 具體目標類狀態
    private int state;
    
    public int getState() {          
    	return state;                
	}                                
                                 
	public void setState(int state) {
    	this.state = state;          
	}                                
}
// 具體觀察者類
public class ConcreteObserver implements Observer {
    
    private Subject subject = new ConcreteSubject();
    private int observerState;
    
    public void update() {
        observerState = subject.getState();
        //	具體更新代碼
    }
}

模式優缺點

觀察者模式的優點

  • 在觀察目標和觀察者之間建立一個抽象的耦合,觀察目標不需要瞭解其具體觀察者,只需知道它們都有一個共同的介面即可
  • 觀察者模式支持廣播通信,觀察目標會向所有註冊的觀察者發出通知,簡化一對多系統設計的難度
  • 觀察者模式符合開閉原則,增加新的觀察者無須修改原有系統代碼,在具體觀察者和觀察目標之間不存在關聯關係的情況下,增加新的目標也很方便

觀察者模式的缺點

  • 如果一個觀察目標有很多直接和間接的觀察者,將所有觀察者都通知到會花費很多時間
  • 如果在觀察者和觀察目標之間有迴圈依賴的話,觀察目標會觸發它們之間的迴圈調用,可能導致系統崩潰
  • 觀察者模式沒有相應機制讓觀察者知道所觀察目標是怎麼發生變化的,而僅僅只是知道觀察目標發生了變化

Java 對觀察者模式的支持

Java 提供了 Observable 類以及 Observer 介面,構成對 Java 語言對觀察者模式的支持

java.util.Observer 介面只定義一個方法,充當抽象觀察者

public interface Observer {

    void update(Observable o, Object arg);
}

當觀察目標狀態發生變化時,該方法將被調用,在 Observer 的實現子類實現該 update() 方法,即具體觀察者可以根據需要有不同的更新行為。當調用觀察目標類 Observable 的 notifyObservers() 方法時,將調用觀察者類中的 update() 方法。

java.util.Observable 類充當觀察目標類,定義了一個向量 Vector 來存儲觀察者對象。標記變數 changed 表示觀察目標是否發生變化,true 表示發生變化,false 則表示對象不再發生改變還已經通知了所有觀察者對象,並調用了它們的 update() 方法。

我們可以直接使用 Observer 介面和 Observable 類來作為觀察者模式的抽象層,自定義具體的觀察者類和觀察目標類,更加方便地在 Java 語言中使用觀察者模式。



您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 方式一: 原生的 js 迴圈 1.while 迴圈 While語句包括一個迴圈條件和一段代碼塊,只要條件為真,就不斷迴圈執行代碼塊。 while (條件) 語句; //舉例: var i = 0; while (i < 100) { console.log('i 當前為:' + i); i = i ...
  • 隨著近些年,web前端開發行業薪資的水漲船高,越來越多的人選擇加入到前端開發行業,他們中的絕大多數人都沒有任何編程基礎,對於這些零基礎的人要怎麼學習web前端開發呢?前端開發都要學習哪些知識。零基礎小白轉行如何入門學習web前端 首先你要知道什麼是web前端,web前端是做什麼的。Web前端做的工作 ...
  • 轉Boolean類型 !!'a' 通過兩個取反,可以強制轉換為Boolean類型。 轉Number類型 // String轉化為Number console.log(+'45'); // 日期輸出時間戳 console.log(+new Date); parseInt ~~,這種方法還可以將字元串轉 ...
  • 因此,我們往往期望,網頁排版時,只做一個語言版的網頁。然後把其它語言的純翻譯文字,每個語言一個文件。 ... 經過一番研究,我找到了辦法。那就是使用 Apache SSI(Server Side Includes)。 ...
  • 下麵就詳細介紹四種方法獲取data-*屬性的值 <li id="getId" data-id="122" data-vice-id="11">獲取id</li> 需要獲取的就是data-id 和 dtat-vice-id的值 一:getAttribute()方法 const getId = docu ...
  • 首先需要找到元素的下標,使用splice函數進行移除: var array = ["zhangsan", "lisi", "wangwu"]; var index = array.indexOf("lisi"); if (index > -1) { array.splice(index, 1); } ...
  • jquery判斷checked的三種方法: .attr('checked); //看版本1.6+返回:”checked”或”undefined” ;1.5-返回:true或false .prop('checked'); //16+:true/false .is(':checked'); //所有版本 ...
  • 概述 Express是目前最流行的基於Node.js的Web開發框架,可以快速地搭建一個完整功能的網站。 Express上手非常簡單,首先新建一個項目目錄,假定叫做hello-world。 $ mkdir hello-world 進入該目錄,新建一個package.json文件,內容如下。 { "n ...
一周排行
    -Advertisement-
    Play Games
  • 前言 插件化的需求主要源於對軟體架構靈活性的追求,特別是在開發大型、複雜或需要不斷更新的軟體系統時,插件化可以提高軟體系統的可擴展性、可定製性、隔離性、安全性、可維護性、模塊化、易於升級和更新以及支持第三方開發等方面的能力,從而滿足不斷變化的業務需求和技術挑戰。 一、插件化探索 在WPF中我們想要開 ...
  • 歡迎ReaLTaiizor是一個用戶友好的、以設計為中心的.NET WinForms項目控制項庫,包含廣泛的組件。您可以使用不同的主題選項對項目進行個性化設置,並自定義用戶控制項,以使您的應用程式更加專業。 項目地址:https://github.com/Taiizor/ReaLTaiizor 步驟1: ...
  • EDP是一套集組織架構,許可權框架【功能許可權,操作許可權,數據訪問許可權,WebApi許可權】,自動化日誌,動態Interface,WebApi管理等基礎功能於一體的,基於.net的企業應用開發框架。通過友好的編碼方式實現數據行、列許可權的管控。 ...
  • Channel 是乾什麼的 The System.Threading.Channels namespace provides a set of synchronization data structures for passing data between producers and consume ...
  • efcore如何優雅的實現按年分庫按月分表 介紹 本文ShardinfCore版本 本期主角: ShardingCore 一款ef-core下高性能、輕量級針對分表分庫讀寫分離的解決方案,具有零依賴、零學習成本、零業務代碼入侵適配 距離上次發文.net相關的已經有很久了,期間一直在從事java相關的 ...
  • 前言 Spacesniffer 是一個免費的文件掃描工具,通過使用樹狀圖可視化佈局,可以立即瞭解大文件夾的位置,幫助用戶處理找到這些文件夾 當前系統C盤空間 清理後系統C盤空間 下載 Spacesniffer 下載地址:https://spacesniffer.en.softonic.com/dow ...
  • EDP是一套集組織架構,許可權框架【功能許可權,操作許可權,數據訪問許可權,WebApi許可權】,自動化日誌,動態Interface,WebApi管理等基礎功能於一體的,基於.net的企業應用開發框架。通過友好的編碼方式實現數據行、列許可權的管控。 ...
  • 一、ReZero簡介 ReZero是一款.NET中間件 : 全網唯一開源界面操作就能生成API , 可以集成到任何.NET6+ API項目,無破壞性,也可讓非.NET用戶使用exe文件 免費開源:MIT最寬鬆協議 , 一直從事開源事業十年,一直堅持開源 1.1 純ReZero開發 適合.Net Co ...
  • 一:背景 1. 講故事 停了一個月沒有更新文章了,主要是忙於寫 C#內功修煉系列的PPT,現在基本上接近尾聲,可以回頭繼續更新這段時間分析dump的一些事故報告,有朋友微信上找到我,說他們的系統出現了大量的http超時,程式不響應處理了,讓我幫忙看下怎麼回事,dump也抓到了。 二:WinDbg分析 ...
  • 開始做項目管理了(本人3年java,來到這邊之後真沒想到...),天天開會溝通整理需求,他們講話的時候忙裡偷閑整理一下常用的方法,其實語言還是有共通性的,基本上看到方法名就大概能猜出來用法。出去打水的時候看到外面太陽好好,真想在外面坐著曬太陽,回來的時候好兄弟三年前送給我的鍵盤D鍵不靈了,在打"等待 ...