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

来源: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
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...