8.觀察者模式:思考與解讀

原文地址:觀察者模式:思考與解讀??更多內容請關注:7.深入思考與解讀設計模式

引言

在開發軟件時,系統的某些狀態可能會發生變化,而你希望這些變化能夠自動通知到依賴它們的其他模塊。你是否曾經遇到過,系統中某個對象發生了變化,但你不想讓其他對象頻繁地去詢問這個變化,或者你不希望每次變化時都手動通知這些對象?

如果可以有一種方式,當對象的狀態發生變化時,所有依賴該對象的其他對象都能自動得到通知并做出響應,這樣的設計是否會讓你的系統更加松耦合且易于維護?

這正是觀察者模式的目的。觀察者模式通過定義一對多的依賴關系,當一個對象的狀態發生改變時,所有依賴于它的對象都會收到通知。你是否覺得,這樣的模式能夠減少系統中對象之間的耦合,使得系統更加靈活?

在這篇文章中,我們將通過一系列問題,逐步引導你理解觀察者模式的核心思想、應用場景以及如何實現它。

什么是觀察者模式?

問題1:在軟件設計中,如果有一個對象發生了變化,你通常如何通知其他依賴它的對象?

假設你有一個系統,其中有一個對象的狀態變化會影響到其他多個對象。你是如何讓這些依賴對象知道這個變化的?是否有一種方法,讓這些對象在不詢問的情況下,自動獲取到最新的狀態?

問題2:你是否曾經使用過“事件監聽器”或“回調”機制?這些方法是否能夠靈活地處理對象之間的依賴關系?

事件監聽器和回調機制在某些場景下是非常有用的,但它們通常會帶來強耦合。而觀察者模式則能夠更靈活地處理一對多的依賴關系。你是否想過,是否有一種設計模式能夠解決這種問題?

觀察者模式正是解決這個問題的一種有效設計模式,它通過“觀察者”來自動通知依賴對象,而不需要依賴對象主動查詢。

觀察者模式的核心概念

問題3:觀察者模式通常由哪些角色組成?每個角色的職責是什么?

觀察者模式包含以下幾個核心角色:

  1. 主題(Subject):被觀察的對象,通常包含一個狀態,當它的狀態發生變化時,會通知所有注冊的觀察者。

  2. 觀察者(Observer):依賴于主題的對象,當主題發生變化時,觀察者會得到通知并進行相應的處理。

  3. 具體主題(ConcreteSubject):實現了主題接口,維護觀察者列表,并在狀態變化時通知它們。

  4. 具體觀察者(ConcreteObserver):實現了觀察者接口,響應主題的變化。

你能理解這些角色是如何相互協作,確保在主題變化時,觀察者能夠自動得到通知嗎?

問題4:在觀察者模式中,主題與觀察者是如何解耦的?它們是如何通過某種機制進行通信的?

觀察者模式的關鍵是,主題不需要知道具體有哪些觀察者,它只需要維護一個觀察者的列表,并在狀態變化時通知所有的觀察者。你能理解,這樣的設計是如何讓系統中的各個部分更加解耦的?

觀察者模式的實現

讓我們通過一個簡單的例子來理解觀察者模式的實現。假設你正在開發一個天氣預報系統,系統中有多個展示天氣信息的應用(例如,手機應用、網頁應用等),當天氣數據發生變化時,所有應用都應該被通知并更新顯示的內容。

步驟1:定義觀察者接口

from abc import ABC, abstractmethod# 觀察者接口
class Observer(ABC):@abstractmethoddef update(self, temperature: float, humidity: float):pass

問題5:觀察者接口(Observer)定義了哪些方法?為什么需要一個統一的接口來保證所有觀察者的行為一致?

觀察者接口定義了一個update()方法,所有觀察者都必須實現這個方法。你是否理解,為什么這種方式能夠確保所有觀察者在狀態變化時都能得到統一的通知,并進行相應的處理?

步驟2:定義主題接口
class Subject(ABC):@abstractmethoddef register_observer(self, observer: Observer):pass@abstractmethoddef remove_observer(self, observer: Observer):pass@abstractmethoddef notify_observers(self):pass

問題6:主題接口(Subject)需要提供哪些方法來管理觀察者?它為什么需要register_observer()remove_observer()notify_observers()方法?

主題接口通過register_observer()remove_observer()notify_observers()來管理觀察者列表。你是否理解,為什么這些方法是觀察者模式的核心?它們如何幫助主題管理觀察者,并在狀態變化時通知所有觀察者?

步驟3:定義具體主題類
class WeatherStation(Subject):def __init__(self):self._observers = []self._temperature = 0.0self._humidity = 0.0def register_observer(self, observer: Observer):self._observers.append(observer)def remove_observer(self, observer: Observer):self._observers.remove(observer)def notify_observers(self):for observer in self._observers:observer.update(self._temperature, self._humidity)def set_weather_data(self, temperature: float, humidity: float):self._temperature = temperatureself._humidity = humidityself.notify_observers()  # 當天氣數據變化時通知所有觀察者

問題7:具體主題類(WeatherStation)如何管理觀察者,并在狀態變化時通知它們?

WeatherStation類維護一個觀察者列表,并通過notify_observers()方法通知所有觀察者。你是否能理解,為什么我們通過set_weather_data()方法來觸發狀態變化,并通知所有的觀察者?

步驟4:定義具體觀察者類
class PhoneApp(Observer):def update(self, temperature: float, humidity: float):print(f"Phone App: Weather updated! Temperature: {temperature}°C, Humidity: {humidity}%")class WebApp(Observer):def update(self, temperature: float, humidity: float):print(f"Web App: Weather updated! Temperature: {temperature}°C, Humidity: {humidity}%")

問題8:具體觀察者類(如PhoneAppWebApp)是如何響應主題的變化的?它們為什么需要實現update()方法?

具體觀察者類實現了update()方法,并在該方法中處理主題狀態變化后的邏輯(如更新顯示)。你是否理解,為什么觀察者需要根據主題的變化來更新自己的狀態,而這種更新是自動觸發的?

步驟5:客戶端代碼
def main():weather_station = WeatherStation()phone_app = PhoneApp()web_app = WebApp()weather_station.register_observer(phone_app)weather_station.register_observer(web_app)weather_station.set_weather_data(25.5, 60)  # 模擬天氣數據變化weather_station.set_weather_data(30.0, 65)  # 再次模擬天氣數據變化if __name__ == "__main__":main()

問題9:在客戶端代碼中,如何通過主題對象來注冊觀察者,并觸發通知?當天氣數據發生變化時,如何確保所有觀察者都能接收到通知?

客戶端通過register_observer()方法注冊觀察者對象,當天氣數據變化時,WeatherStation通過調用notify_observers()通知所有注冊的觀察者。你是否理解,這種機制如何保證了在狀態變化時,所有依賴對象(觀察者)都能自動響應?

觀察者模式的優缺點

問題10:觀察者模式的優點是什么?它如何幫助我們解耦系統中的不同模塊?

觀察者模式通過將主題與觀察者解耦,避免了直接依賴關系,使得系統更加靈活和可擴展。你是否理解,這種設計如何幫助你在不修改主題類的情況下,輕松增加新的觀察者?同時,觀察者也不需要知道主題類的具體實現。

問題11:觀察者模式的缺點是什么?它在某些情況下是否會導致性能問題或過度通知?

雖然觀察者模式非常靈活,但在某些情況下,當有大量觀察者時,可能會導致性能問題。你是否認為,如果觀察者數量過多,通知的效率可能成為瓶頸?或者,是否可能存在不必要的通知?

適用場景

問題12:你能想到哪些場景,觀察者模式能夠發揮作用?

觀察者模式適用于以下場景:

  • 當多個對象依賴于某個對象的狀態變化時。

  • 當你需要將事件驅動機制引入系統時。

你能想到其他場景嗎?例如,用戶界面的事件監聽、股票市場的價格更新等,是否也可以使用觀察者模式?

問題13:觀察者模式是否適用于所有場景?在某些情況下,是否有更合適的設計模式?

觀察者模式適用于需要多方監聽和響應的場景,但如果對象之間的依賴較少,或者沒有動態更新的需求,是否可以使用更簡單的設計模式?

接下來,我們將通過具體的代碼示例來加深理解觀察者模式。

觀察者模式深入解讀

一、引言

觀察者模式(Observer Pattern)是一種行為型設計模式,它定義了一種一對多的依賴關系,讓多個觀察者對象同時監聽某個主題對象。當主題對象的狀態發生變化時,所有依賴于它的觀察者都會得到通知并自動更新。觀察者模式通常用于事件處理系統,比如用戶界面、消息通知系統等。


二、簡單理解:什么是觀察者模式?

1. 什么是觀察者模式?

觀察者模式的核心思想是:當一個對象的狀態發生改變時,所有依賴于它的對象都會自動得到通知并更新。這個模式常用于一種場景:當數據發生變化時,其他對象需要得到變化的通知并作出響應。

通俗地講,觀察者模式就像是你訂閱了一家新聞網站,每當該網站發布了新的新聞,你就會收到通知。你不需要主動去檢查網站是否有新新聞,而是當新聞發生變化時,系統會自動通知你。

2. 觀察者模式的組成部分

觀察者模式通常包含以下幾個部分:

  • 主題(Subject):維護一組觀察者,并在狀態變化時通知所有觀察者。

  • 觀察者(Observer):定義一個更新接口,供主題在狀態發生變化時調用。

  • 具體主題(ConcreteSubject):實現主題接口,保存狀態,并在狀態變化時通知觀察者。

  • 具體觀察者(ConcreteObserver):實現觀察者接口,根據主題的狀態變化做出響應。


三、用自己的話解釋:如何理解觀察者模式?

1. 類比實際生活中的場景

假設你是一家雜志的訂閱者,每當這本雜志出版新一期時,你都會收到郵件通知。這時候,你是“觀察者”,雜志社是“主題”,每當雜志社發布新內容時,它會通知所有訂閱的讀者。

在編程中,觀察者模式通常應用于一對多的關系,當一個對象的狀態變化時,其他對象會被通知并自動更新。比如,用戶界面中一個輸入框的值發生變化時,其他依賴于這個輸入框值的組件(如顯示結果的標簽、搜索框等)會自動更新。

2. 為什么要使用觀察者模式?

觀察者模式的主要優勢在于,它通過松耦合的方式管理對象之間的依賴關系。觀察者與主題之間沒有直接的連接,主題只知道觀察者的接口,不知道具體的實現,這使得系統更加靈活和可擴展。


四、深入理解:觀察者模式的實現

接下來,我們通過一個具體的代碼示例來實現觀察者模式,幫助你更好地理解如何在代碼中使用這個模式。

示例:新聞推送系統

假設我們要開發一個新聞推送系統,用戶可以訂閱不同的新聞類型。當有新的新聞發布時,所有訂閱該類型的用戶都會收到通知。

1. 定義觀察者接口
# 觀察者接口:定義更新方法
class Observer:def update(self, message: str):pass
2. 定義主題接口
# 主題接口:定義注冊、移除觀察者的方法
class Subject:def register_observer(self, observer: Observer):passdef remove_observer(self, observer: Observer):passdef notify_observers(self):pass
3. 定義具體主題類:新聞發布
# 具體主題類:新聞發布
class NewsPublisher(Subject):def __init__(self):self._observers = []self._latest_news = ""def register_observer(self, observer: Observer):self._observers.append(observer)def remove_observer(self, observer: Observer):self._observers.remove(observer)def notify_observers(self):for observer in self._observers:observer.update(self._latest_news)def set_latest_news(self, news: str):self._latest_news = newsself.notify_observers()  # 狀態變化時通知所有觀察者
4. 定義具體觀察者類:用戶
# 具體觀察者類:用戶
class User(Observer):def __init__(self, name: str):self._name = namedef update(self, message: str):print(f"{self._name} received news: {message}")
5. 客戶端代碼:訂閱新聞
# 創建新聞發布者實例
news_publisher = NewsPublisher()# 創建用戶實例
user1 = User("Alice")
user2 = User("Bob")# 用戶訂閱新聞
news_publisher.register_observer(user1)
news_publisher.register_observer(user2)# 發布新聞
news_publisher.set_latest_news("Breaking: New python version released!")# 取消某個用戶的訂閱
news_publisher.remove_observer(user2)# 再次發布新聞
news_publisher.set_latest_news("Update: Python tutorial available!")
代碼解析:
  1. Observer?類:這是觀察者接口,定義了?update?方法,主題會通過這個方法通知觀察者更新。

  2. Subject?類:這是主題接口,定義了?register_observerremove_observer?和?notify_observers?方法,用來管理觀察者的注冊、移除和通知。

  3. NewsPublisher?類:這是具體的主題類,繼承了?Subject,并實現了管理觀察者和通知更新的邏輯。當有新的新聞發布時,它會調用?notify_observers?方法,通知所有注冊的觀察者。

  4. User?類:這是具體的觀察者類,繼承了?Observer,當主題狀態變化時,通過?update?方法接收到新新聞的通知。

  5. 客戶端代碼:創建新聞發布者和多個用戶,用戶訂閱新聞發布,發布者通知所有訂閱的用戶更新。


五、解釋給別人:如何講解觀察者模式?

1. 用簡單的語言解釋

觀察者模式就像是你訂閱了一個新聞頻道,每當有新新聞發布時,你都會收到通知。你不需要時刻查看新聞網站,而是當新聞變化時,你會自動收到更新。它讓你能跟隨某個變化的對象,而不用頻繁地查詢該對象。

2. 為什么要使用觀察者模式?

使用觀察者模式的好處是,它讓系統的各個組件之間通過接口解耦。主題和觀察者之間沒有直接的依賴,主題只知道觀察者的接口,并且通過通知機制來更新觀察者。當有新觀察者加入或離開時,主題不需要做太多改動,從而提高了系統的靈活性和擴展性。


六、總結

通過一系列問題的引導,我們逐步理解了觀察者模式的核心思想和實現方式。觀察者模式通過定義一對多的依賴關系,使得當一個對象的狀態發生變化時,所有依賴對象能夠自動接收到通知并做出響應。它讓系統更加松耦合,靈活且可擴展。

通過以上過程,我們可以得出以下結論:

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

  • 它的主要優點是解耦、靈活性和擴展性,尤其適用于事件驅動或消息推送的場景。

  • 觀察者模式的應用非常廣泛,例如 GUI 組件、事件處理、推送通知系統等。

觀察者模式的優點:
  • 松耦合:觀察者和主題之間沒有直接依賴,便于擴展和維護。

  • 擴展性:可以靈活地增加或移除觀察者,而不影響主題。

  • 易于實現:適合實現一對多的通信關系,尤其在實時更新場景中非常有用。

觀察者模式的缺點:
  • 可能導致性能問題:如果觀察者數量非常多,可能會影響通知效率。

  • 依賴復雜:如果觀察者和主題之間有太多復雜的依賴,可能會導致系統的理解和維護難度增加。

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

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

相關文章

【HD-RK3576-PI】Ubuntu桌面多顯、旋轉以及更新Logo

硬件:HD-RK3576-PI 軟件:Linux6.1Ubuntu22.04 在基于HD-RK3576-PI硬件平臺運行Ubuntu 22系統的開發過程中,屏幕方向調整是提升人機交互體驗的關鍵環節。然而,由于涉及uboot引導階段、內核啟動界面、桌面環境顯示全流程適配&#x…

Rsync+sersync2實現目錄實時同步

Sersync rsync 實現實時同步服務 sersync2二進制包目錄規劃 /app/tools/sersync/ /app/tools/sersync/bin /app/tools/sersync/conf項目架構是這樣的: ------------------- ------------------- ------------------- | | …

MySQL視圖高級應用與最佳實踐

1. 視圖與索引的協同優化?? ??物化視圖(模擬實現)?? MySQL原生不支持物化視圖,但可通過“定時刷新”的物理表模擬: -- 1. 創建存儲結果的物理表 CREATE TABLE cached_monthly_sales (product_id INT,total_sales DECIMAL(10…

string的模擬實現 (6)

目錄 1.string.h 2.string.cpp 3.test.cpp 4.一些注意點 本篇博客就學習下如何模擬實現簡易版的string類&#xff0c;學好string類后面學習其他容器也會更輕松些。 代碼實現如下&#xff1a; 1.string.h #define _CRT_SECURE_NO_WARNINGS 1 #pragma once #include <…

Unity:像素(Pixels) 和 單位(Units)

目錄 從第一性原理出發&#xff1a;什么是像素和 Unit&#xff1f; &#x1f9f1; 1. 像素&#xff08;Pixel&#xff09;&#xff1a;圖像的最小單位 &#x1f4d0; 2. Unity Unit&#xff08;單位&#xff09;&#xff1a;游戲世界中的度量單位 核心換算公式&#xff1a;…

【失敗總結】Win10系統安裝docker

1.啟用或關閉windows功能中&#xff0c;將Hyper-V功能勾選全部啟用&#xff0c;容器勾選。設置好后要重啟電腦。 2.管網下載下載安裝Docker  Docker官網&#xff1a;https://www.docker.com/ 3.可以自定義Docker安裝路徑 新建安裝目錄&#xff1a;d:\MySoftware\Docker并將D…

《Adaptive Layer-skipping in Pre-trained LLMs》- 論文筆記

作者&#xff1a;Xuan Luo, Weizhi Wang, Xifeng Yan Department of Computer Science, UC Santa Barbara xuan_luoucsb.edu, weizhiwangucsb.edu, xyancs.ucsb.edu 1. 引言與動機 1.1 背景 LLM 的成功與挑戰: 大型語言模型 (LLMs) 在翻譯、代碼生成、推理等任務上取得巨大成…

DQN在Gym的MountainCar環境的實現

DQN on MountainCar 引言 在本次實驗里&#xff0c;我構建了DQN和Dueling DQN&#xff0c;并在Gymnasium庫的MountainCar環境中對它們展開測試。我通過調整訓練任務的超參數&#xff0c;同時設計不同的獎勵函數及其對應參數&#xff0c;致力于獲取更優的訓練效果。最后&#…

計算機網絡綜合實驗指南

計算機網絡綜合實驗指南 本實驗將結合《計算機網絡自頂向下》前三章的核心概念&#xff0c;通過實際操作加深對應用層、運輸層和網絡層的理解。實驗涵蓋 HTTP/TCP抓包分析、DNS解析觀察、網頁性能評估及簡單Socket編程&#xff0c;幫助你將理論轉化為實踐。 實驗準備 工具&…

【AI部署】騰訊云GPU-RUN—SadTalker的AI數字人視頻—未來之窗超算中心

磁盤空間 創建未來之窗 查看磁盤命令 df -h 指定路徑創建環境 conda create --prefix sadtalker python3.10 指令路徑運行環境 conda activate ./sadtalker 安裝環境 pip install torch1.12.1cu113 torchvision0.13.1cu113 torchaudio0.12.1 --extra-index-url https://…

爬蟲利器SpiderTools谷歌插件教程v1.0.0!!!web端JavaScript環境檢測!!!

SpiderTools谷歌插件教程v1.0.0 一、SpiderTools簡介二、下載通道三、插件介紹四、插件使用五、工具函數使用 補環境工具推薦&#xff1a;爬蟲補環境利器webEnv 一、SpiderTools簡介 SpiderTools主要用于檢測和監控網頁的JavaScript運行環境。該插件可以幫助開發者更好地查看…

Android開發協調布局滑動懸停

Android開發協調布局滑動懸停 直接給個xml,防止下次忘了怎么寫。 <?xml version="1.0" encoding="utf-8"?> <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"x…

Linux學習——TCP

一.TCP編程API 1.socket函數 1.socket函數 include include int socket(int domain,int type,int protocol); 參數 domain AF_INET AF_INET6 AF_UNIX,AF_LOCAL AF_NETLINK AF_PACKET type SOCK_STREAM: 流式…

Linux驅動開發--異步通知與異步I/O

3、異步通知與異步I/O 3.1 Linux信號 阻塞與非阻塞訪問、poll()函數提供了較好的解決設備訪問的機制&#xff0c;但是如果有了異步通知&#xff0c;整套機制則更加完整了。 異步通知的意思是&#xff1a;一旦設備就緒&#xff0c;則主動通知應用程序&#xff0c;這樣應用程序…

大語言模型推理能力的強化學習現狀理解GRPO與近期推理模型研究的新見解

每周跟蹤AI熱點新聞動向和震撼發展 想要探索生成式人工智能的前沿進展嗎&#xff1f;訂閱我們的簡報&#xff0c;深入解析最新的技術突破、實際應用案例和未來的趨勢。與全球數同行一同&#xff0c;從行業內部的深度分析和實用指南中受益。不要錯過這個機會&#xff0c;成為AI領…

【Linux系統】Linux基礎指令(詳解Linux命令行常用指令,每一個指令都有示例演示)

文章目錄 一、與文件路徑相關的指令0.補充知識&#xff1a;路徑的認識1.pwd 指令2.cd 指令&#xff08;含家目錄的介紹&#xff09; 二、創建和刪除文件的指令0.補充知識&#xff1a;普通文件和目錄文件1.touch 指令&#xff08;可以修改文件的時間戳&#xff09;2.mkdir 指令3…

LangChain 單智能體模式示例【純代碼】

# LangChain 單智能體模式示例import os from typing import Anyfrom langchain.agents import AgentType, initialize_agent, Tool from langchain_openai import ChatOpenAI from langchain.tools import BaseTool from langchain_experimental.tools.python.tool import Pyt…

解決:VSCode C++ conan 安裝第三方庫后 頭文件報錯

文章目錄 1 頭文件include路徑查找報錯參考 1 頭文件include路徑查找報錯 找到conan_toolchain.cmake中 INCLUDE_PATH list(PREPEND CMAKE_INCLUDE_PATH "/Users/hanliqiang/.conan2/p/b/fmte8c4f7a755477/p/include")生成C編譯配置 CtrlShiftP 中選擇C Edit Confi…

松靈Cobot Magic雙臂具身遙操機器人(基于ROS的定位建圖與協同導航技術)

摘要 本文以CobotMagic可移動協作機器人為研究對象&#xff0c;從硬件架構設計、軟件系統架構、多傳感器融合定位建圖系統、智能導航系統協同機制四個維度&#xff0c;深入解析機器人系統工作原理。重點研究多傳感器融合定位建圖系統實現原理&#xff0c;結合實測數據驗證系統…

回歸,git 分支開發操作命令

核心分支說明 主分支&#xff08;master/production&#xff09;存放隨時可部署到生產環境的穩定代碼&#xff0c;僅接受通過測試的合并請求。 開發分支&#xff08;develop&#xff09;集成所有功能開發的穩定版本&#xff0c;日常開發的基礎分支&#xff0c;從該分支創建特性…