【設計模式】觀察者模式 (發布-訂閱模式,模型-視圖模式,源-監聽器模式,從屬者模式)

觀察者模式(Observer Pattern)詳解


一、觀察者模式簡介

觀察者模式(Observer Pattern) 是一種 行為型設計模式(對象行為型模式),它定義了一種一對多的依賴關系,讓多個觀察者對象同時監聽某一個主題對象。當主題對象發生變化時,它的所有依賴者(觀察者)都會收到通知并自動更新。

你可以把它想象成“訂閱-發布”機制:當你訂閱了一份雜志后,每當有新一期出版時,你就會收到通知。這里的“你”就是觀察者,“雜志”則是被觀察的主題。

在這里插入圖片描述
交通信號燈 <- -> 觀察目標
汽車(汽車駕駛員) <- -> 觀察者
(一對多)

軟件系統:一個對象的狀態或行為的變化將導致其他對象的狀態或行為也發生改變,它們之間將產生聯動。

觀察者模式

  1. 定義了對象之間一種一對多的依賴關系,讓一個對象的改變能夠影響其他對象
  2. 發生改變的對象稱為觀察目標,被通知的對象稱為觀察者
  3. 一個觀察目標可以對應多個觀察者

別名
發布-訂閱(Publish/Subscribe)模式
模型-視圖(Model/View)模式
源-監聽器(Source/Listener)模式
從屬者(Dependents)模式

觀察者模式包含以下4個角色
Subject(目標)
ConcreteSubject(具體目標)
Observer(觀察者)
ConcreteObserver(具體觀察者)


二、解決的問題類型

觀察者模式主要用于解決以下問題:

  • 狀態變化需要通知相關聯的對象:例如,用戶界面中的模型數據發生變化時,視圖需要自動更新。需要在系統中創建一個觸發鏈。
  • 避免緊耦合:允許對象之間保持松散的聯系,無需直接相互引用。一個抽象模型有兩個方面,其中一個方面依賴于另一個方面,將這兩個方面封裝在獨立的對象中使它們可以各自獨立地改變和復用。
  • 實現廣播通信:可以向所有注冊的觀察者發送消息,而不需要知道它們的具體身份。

三、使用場景

場景示例
GUI事件處理如按鈕點擊觸發窗口刷新
消息傳遞系統如發布/訂閱模式的消息隊列
數據模型與視圖同步MVC架構中,Model數據變化時通知View更新
實時數據監控如股票價格變動時通知所有關注的客戶端

四、核心概念

  1. Subject(主題/被觀察者):維護了一個觀察者列表,并提供方法供觀察者注冊或移除自身;在狀態改變時通知所有觀察者。
  2. Observer(觀察者):接收來自主題的通知并作出響應。
  3. ConcreteSubject & ConcreteObserver:具體實現上述兩個接口的實際類。

五、實際代碼案例(Java)

1. 定義 Observer 接口

// 觀察者接口
public interface Observer {void update(float temperature, float humidity, float pressure);
}

2. 定義 Subject 接口

import java.util.ArrayList;// 主題接口
public interface Subject {void registerObserver(Observer o); // 注冊觀察者void removeObserver(Observer o);   // 移除觀察者void notifyObservers();            // 通知所有觀察者
}

3. 創建具體的 WeatherData 類作為被觀察者

public class WeatherData implements Subject {private ArrayList<Observer> observers;private float temperature;private float humidity;private float pressure;public WeatherData() {observers = new ArrayList<>();}@Overridepublic void registerObserver(Observer o) {observers.add(o);}@Overridepublic void removeObserver(Observer o) {int i = observers.indexOf(o);if (i >= 0) {observers.remove(i);}}@Overridepublic void notifyObservers() {for (Observer observer : observers) {observer.update(temperature, humidity, pressure);}}public void measurementsChanged() {notifyObservers();}public void setMeasurements(float temperature, float humidity, float pressure) {this.temperature = temperature;this.humidity = humidity;this.pressure = pressure;measurementsChanged();}
}

4. 創建具體的觀察者類

public class CurrentConditionsDisplay implements Observer {private float temperature;private float humidity;private Subject weatherData;public CurrentConditionsDisplay(Subject weatherData) {this.weatherData = weatherData;weatherData.registerObserver(this);}@Overridepublic void update(float temperature, float humidity, float pressure) {this.temperature = temperature;this.humidity = humidity;display();}public void display() {System.out.println("Current conditions: " + temperature + "F degrees and " + humidity + "% humidity");}
}

5. 測試代碼

public class WeatherStation {public static void main(String[] args) {WeatherData weatherData = new WeatherData();CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);weatherData.setMeasurements(80, 65, 30.4f);weatherData.setMeasurements(82, 70, 29.2f);weatherData.setMeasurements(78, 90, 29.2f);}
}

輸出結果:

Current conditions: 80.0F degrees and 65.0% humidity
Current conditions: 82.0F degrees and 70.0% humidity
Current conditions: 78.0F degrees and 90.0% humidity

典型代碼

典型的抽象目標類代碼

using System.Collection
abstract class Subject
{//定義一個觀察者集合用于存儲所有觀察者對象
protected ArrayList observers = new ArrayList();
//聲明抽象注冊方法,用于向觀察者集合中增加一個觀察者public abstract void Attach(Observer observer);
//聲明抽象注銷方法,用于在觀察者集合中刪除一個觀察者public abstract void Detach(Observer observer);//聲明抽象通知方法public abstract void Notify();
}

典型的具體目標類代碼

class ConcreteSubject : Subject
{public override void Attach(Observer observer){observers.Add(observer);}public override void Detach(Observer observer){observers.Remove(observer);}//實現通知方法public override void Notify(){//遍歷觀察者集合,調用每一個觀察者的響應方法foreach(object obs in observers){((Observer)obs).Update();}}
}

典型的抽象觀察者代碼

interface Observer
{void Update();
}

典型的具體觀察者代碼

class ConcreteObserver : Observer
{//實現響應方法public void Update(){//具體更新代碼}
}

典型的客戶端代碼片段

……
Subject subject = new ConcreteSubject();
Observer observer = new ConcreteObserver();
subject.Attach(observer);
subject.Notify();
……

其他案例

  1. 在某多人聯機對戰游戲中,多個玩家可以加入同一戰隊組成聯盟,當戰隊中的某一成員受到敵人攻擊時將給所有其他盟友發送通知,盟友收到通知后將做出響應。
    現使用觀察者模式設計并實現該過程,以實現戰隊成員之間的聯動。
    戰隊成員之間的聯動過程:
    聯盟成員受到攻擊 --> 發送通知給盟友 --> 盟友做出響應
    在這里插入圖片描述

在這里插入圖片描述

  1. 貓、狗與老鼠
    假設貓是老鼠和狗的觀察目標,老鼠和狗是觀察者,貓叫老鼠跑,狗也跟著叫,使用觀察者模式描述該過程。

在這里插入圖片描述


六、優缺點分析

優點描述
? 解耦主題和觀察者之間是松散耦合的,便于獨立開發和修改。可以實現表示層和數據邏輯層的分離。
? 支持廣播通信可以輕松地將信息廣播給多個觀察者
? 易于擴展新增觀察者只需實現 Observer 接口即可,不影響現有代碼。符合開閉原則,增加新的具體觀察者無須修改原有系統代碼,在具體觀察者與觀察目標之間不存在關聯關系的情況下,增加新的觀察目標也很方便
缺點描述
? 可能導致性能問題如果觀察者數量過多,通知過程可能會變得緩慢
? 潛在的內存泄漏風險若未正確移除觀察者,可能導致無法釋放資源。如果存在循環依賴時可能導致系統崩潰
? 難以追蹤依賴關系復雜的觀察者網絡可能使程序難以調試和理解
其他沒有相應的機制讓觀察者知道所觀察的目標對象是怎么發生變化的,而只是知道觀察目標發生了變化

七、與其他模式對比(補充)

模式名稱目標
命令模式封裝請求為對象,支持請求排隊、日志記錄等操作
中介者模式減少對象之間的直接交互,通過中介者進行協調
觀察者模式維持對象間的一對多依賴關系,實現狀態變更的通知

八、最終小結

觀察者模式是一種非常實用的設計模式,特別適合那些需要在不同組件之間維持松散耦合并且能夠響應狀態變化的應用場景。通過合理地運用觀察者模式,我們可以在不破壞原有模塊獨立性的前提下,有效地實現組件間的協作與通信。

在開發圖形用戶界面(GUI)、實時數據監控系統、消息中間件等項目時,觀察者模式能夠幫助你更好地組織代碼結構,提高系統的靈活性和可維護性。


📌 一句話總結:

觀察者模式就像一個新聞通訊社,一旦有新的新聞發布,所有訂閱了該新聞的人都會立即收到通知。

部分內容由AI大模型生成,注意識別!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/90331.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/90331.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/90331.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Linux的`<< EOF`(Here-Document)詳解多回答筆記250722

Linux的<< EOF(Here-Document)詳解多回答筆記250722 Linux 中的 << EOF 結構稱為 Here Document&#xff08;立即文檔或嵌入文檔&#xff09;&#xff0c;它是一種在 Shell 腳本中直接嵌入多行文本輸入流&#xff08;通常作為命令的標準輸入&#xff09;的方式。E…

Go語言實戰案例-簡單配置文件(INI格式)解析器

以下是《Go語言100個實戰案例》中的 文件與IO操作篇 - 案例20&#xff1a;簡單配置文件&#xff08;INI格式&#xff09;解析器 的完整內容&#xff0c;適合入門學習如何用 Go 語言解析常見的 .ini 配置文件格式。&#x1f3af; 案例目標使用 Go 語言解析一個 .ini 格式的配置文…

用 PyTorch 實現全連接網絡識別 MNIST 手寫數字

目錄 一、什么是全連接網絡 二、代碼實現步驟 1. 導入必要的庫 2. 數據準備 3. 定義網絡結構 4. 模型訓練 5. 模型保存和加載 6. 預測單張圖片 7. 主函數 三、運行結果說明 四、小結 一、什么是全連接網絡 全連接神經網絡&#xff08;Fully Connected Neural Networ…

vscode怎么安裝MINGW

下載&#xff1a; 第一步選擇MINGW官網&#xff1a;MinGW-w64 - for 32 and 64 bit Windows - SourceForge.net 點擊Files 點擊Toolchains targetting Win64 點擊第一個 Personal Builds 點擊mingw-builds 選擇8.1.0 點擊第二個 threads-posix 點擊第二個seh 最后左鍵點擊下…

CSS圖片分層設置

在CSS中實現圖片分層效果&#xff0c;主要通過定位屬性和層疊上下文控制。以下是核心實現方法和示例&#xff1a; 一、核心實現原理定位方式 使用 position: relative/absolute/fixed 使圖片脫離文檔流 .layer {position: absolute; /* 關鍵屬性 */top: 0;left: 0; }層疊控制 通…

GEMINUS 和 Move to Understand a 3D Scene

論文鏈接&#xff1a;https://arxiv.org/abs/2507.14456 代碼鏈接&#xff1a;https://github.com/newbrains1/GEMINUS 端到端自動駕駛的挑戰 端到端自動駕駛是一種“一站式”方法&#xff1a;模型直接從傳感器輸入&#xff08;如攝像頭圖像&#xff09;生成駕駛軌跡或控制信號…

算法與數據結構:線性表

C語言數據結構基礎&#xff1a;線性表詳解線性表是數據結構中最基礎、最常用的形式&#xff0c;就像一列整齊排隊的游客&#xff1a;每個元素有固定位置&#xff08;前驅和后繼&#xff09;&#xff0c;長度可動態變化。在C語言中&#xff0c;它主要通過順序表&#xff08;數組…

制作mac 系統U盤

使用 installinstallmacos.py&#xff08;更兼容&#xff09; 蘋果官方不提供所有歷史版本的安裝器&#xff0c;但可以通過一個開源腳本下載&#xff08;Apple 提供的企業支持工具&#xff09;&#xff1a; git clone https://github.com/munki/macadmin-scripts.git cd macadm…

滲透部分總結

docker環境搭建以及dns等原理講解Docker搭建&#xff1a;Linux 系統上安裝 Docker 引擎并啟動服務&#xff1a;# 安裝Docker引擎 curl -fsSL https://get.docker.com | sh 通過 curl 下載并執行 Docker 官方的安裝腳本&#xff0c;這會自動配置 Docker 倉庫并安裝最新版本的 Do…

k8s pvc是否可綁定在多個pod上

1.pvc是否可綁定在多個podPVC 是否能被多個 Pod 使用&#xff0c;取決于它的 accessModes。PVC 的 accessModes是否支持多個 Pod 同時使用說明ReadWriteOnce (RWO)? 若多個Pod&#xff0c;需在相同節點上&#xff08;僅允許被單個節點上的Pod掛載&#xff09;常用于本地磁盤、…

如何加固Endpoint Central服務器的安全?(下)

Endpoint Central 作為企業終端管理的 “中樞系統”&#xff0c;掌控著全網終端的補丁推送、軟件部署、配置管理、遠程控制等關鍵權限&#xff0c;存儲著大量終端資產信息、用戶數據及企業策略配置。一旦服務器被攻破&#xff0c;攻擊者可能篡改管理指令&#xff08;如推送惡意…

信息整合注意力IIA,通過雙方向注意力機制重構空間位置信息,動態增強目標關鍵特征并抑制噪聲

在遙感圖像語義分割等視覺任務中&#xff0c;編碼器 - 解碼器結構通過跳躍連接融合多尺度特征時&#xff0c;常面臨兩大挑戰&#xff1a;一是編碼器的局部細節特征與解碼器的全局語義特征融合時&#xff0c;空間位置信息易丟失&#xff0c;導致目標定位不準&#xff1b;二是復雜…

如何遷移jenkins至另一臺服務器

前言公司舊的服務器快到期了&#xff0c;需要將部署在其上的jenkins整體遷移到另一臺服務器&#xff0c;兩臺都是aws ec2服務器。文章主要提供給大家一種遷移思路&#xff0c;并不一定是最優解&#xff0c;僅供參考&#xff0c;大家根據實際情況自行選用和修改&#xff0c;舉一…

在vue中遇到Uncaught TypeError: Assignment to constant variable(常亮無法修改)

1.問題如下:2.出現這個問題的原因----在設計變量的時候采用了const來進行修飾,在修改的時候直接對其進行修改3.利用響應式變量的特點&#xff0c;修改為下面這樣就可以正常了

RCE隨筆-奇技淫巧(2)

Linux命令長度限制在7個字符的情況下&#xff0c;如何拿到shell <?php $param $_REQUEST[param]; If ( strlen($param) < 8 ) { echo shell_exec($param); }分析代碼&#xff1a;這段代碼傳入參數param然后進入if語句判斷是否小于8個字符&#xff0c;然后如果小于就會進…

設計模式九:構建器模式 (Builder Pattern)

動機(Motivation)1、在軟件系統中&#xff0c;有時候面臨著“一個復雜對象”的創建工作&#xff0c;其通常由各個部分的子對象用一定的算法構成&#xff1b;由于需求的變化&#xff0c;這個復雜對象的各個部分經常面臨著劇烈的變化&#xff0c;但是將它們組合在一起的算法卻相對…

如何高效合并音視頻文件

在自我學習或者進行視頻剪輯的時候&#xff0c;經常從資源網址下載音視頻分離的文件&#xff0c;例如audio_file1.m4a和video_1.mp4&#xff0c;之后需要把這兩個文件合并在一起。于是條件反射得想要利用剪映等第三方工具&#xff0c;進行音視頻的封裝。可惜不幸的是&#xff0…

虛幻 5 與 3D 軟件的協作:實時渲染,所見所得

《曼達洛人》的星際飛船在片場實時掠過虛擬荒漠&#xff0c;游戲開發者拖動滑塊就能即時看到角色皮膚的通透變化&#xff0c;實時渲染技術正以 “所見即所得” 的核心優勢&#xff0c;重塑著 3D 創作的整個邏輯。虛幻引擎 5&#xff08;UE5&#xff09;憑借 Lumen 全局光照和 N…

?Eyeriss 架構中的訪存行為解析(騰訊元寶)

?Eyeriss 架構中的訪存行為解析?Eyeriss 是 MIT 提出的面向卷積神經網絡&#xff08;CNN&#xff09;的能效型 NPU&#xff08;神經網絡處理器&#xff09;架構&#xff0c;其核心創新在于通過硬件結構優化訪存行為&#xff0c;以解決傳統 GPU 在處理 CNN 時因數據搬運導致的…

數字圖像處理(三:圖像如果當作矩陣,那加減乘除處理了矩陣,那圖像咋變):從LED冬奧會、奧運會及春晚等等大屏,到手機小屏,快來挖一挖里面都有什么

數字圖像處理&#xff08;三&#xff09;一、&#xff08;準備工作&#xff1a;咋玩&#xff0c;用什么玩具&#xff09;圖像以矩陣形式存儲&#xff0c;那矩陣一變、圖像立刻跟著變&#xff1f;1. Python Jupyter Notebook/Lab 庫 (NumPy, OpenCV, Matplotlib, scikit-image…