【觀察者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實現

簡介

觀察者模式(Observer Pattern)是一種行為型模式。它定義對象間的一種一對多的依賴關系,當一個對象的狀態發生改變時,所有依賴于它的對象都得到通知并被自動更新。

觀察者模式使用三個類Subject、Observer和Client。Subject對象帶有綁定觀察者到Client對象和從Client對象解綁觀察者的方法。我們創建Subject類、Observer抽象類和擴展了抽象類Observer的實體類。

作用

  1. 一個對象狀態更新,其他依賴對象收到通知和自動更新的機制。
  2. 實現模塊化分離,實現主題與觀察者交互對象之間的松耦合。
    1)觀察者定義了對象之間一對多的關系。
    2)被觀察者(主題)用一個共同的接口來更新觀察者。
    3)觀察者和被觀察者用松耦合方式結合,被觀察者不知道觀察者的細節,只知道觀察者實現了觀察者接口。

實現步驟

  1. 創建觀察者observer基礎接口,包含主題和更新方法
  2. 創建主題subject抽象類,包含observer列表以及添加和刪除方法
  3. 創建具體的主題類,實現通知方法,發布通知時輪詢通知全部觀察者
  4. 創建多個具體觀察者,與主題關聯,并實現自己的更新方法
  5. 客戶調用時先聲明主題,再將觀察者分別添加到主題,當主題發布通知時,觀察者自動更新

UML

?

Java代碼

觀察者接口

 
// ObserverAPI.java 觀察者接口,Java 9已經默認支持Observer接口
// 這里避免沖突采取ObserverAPI命名
public interface ObserverAPI {
public Subject subject = null;
public void update(String content);
}

具體觀察者

 
// ConcreteObserver.java 具體的觀察者實現類,也可以看成訂閱者,關聯對應的主題類。
// 不同的觀察者也可以對應多個主題
public class ConcreteObserver implements ObserverAPI {
public Subject subject;
// 給觀察者綁定主題,同時把觀察者添加到主題列表
public ConcreteObserver(Subject subject) {
this.subject = subject;
this.subject.register((ObserverAPI) this);
}
// 觀察者發出更新通知,不用單獨告訴訂閱者,由訂閱者自行監聽
public void update(String content) {
System.out.println(String.format("%s::update() [subject.name = %s content = %s]",
this.getClass().getName(),
this.subject.getName(), content));
}
}

 
// ConcreteObserver2.java 具體的觀察者實現類,也可以看成訂閱者,關聯對應的主題類。
// 不同的觀察者可以對應不同的主題。
public class ConcreteObserver2 implements ObserverAPI {
// 這里沒有在構造器就綁定某個主題,而是從客戶角度去注冊觀察者
public ConcreteObserver2() {
}
// 觀察者發出更新通知,觀察者自行監聽
public void update(String content) {
System.out.println(String.format("%s::update() [content = %s]",
this.getClass().getName(), content));
}
}

抽象主題類

 
// Subject.java 定義抽象主題類或者接口,供具體主題類繼承
public abstract class Subject {
private String name;
// protected Set<ObserverAPI> observers = new HashSet<>();
protected List<ObserverAPI> observers = new ArrayList<>();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void register(ObserverAPI observer) {
System.out.println(this.getClass().getName() + "::register() [observer = " + observer.getClass().getSimpleName() + "]");
observers.add(observer);
}
public void remove(ObserverAPI observer) {
observers.remove(observer);
}
// 通知由具體類來實現邏輯
public abstract void notify(String name);
}

具體主題類

 
// ConcreteSubject.java 觀察者主題類,也是發布者,重寫具體的通知方法。不同主題可以關聯不同的觀察者。
public class ConcreteSubject extends Subject {
public ConcreteSubject(String name) {
this.setName(name);
}
// 不同的主題類有自己的通知方法,批量通知綁定的觀察者
@Override
public void notify(String content) {
System.out.println(this.getClass().getName() + "::notify() [content = " + content + "]");
for (Object observer : this.observers) {
((ObserverAPI) observer).update(content);
}
}
}

測試調用

 
/**
* 觀察者模式應用非常廣泛,主要是觀察者提前綁定到發布者
* 當發布者發布消息時,批量廣播通知,而無需逐一通知
* 觀察者監聽到消息后自己決定采取哪一種行為
*/
// 定義一個主題,也就是發布者
Subject concreteSubject = new ConcreteSubject("subject1");
// 再聲明觀察者,通過構造器注冊到主題上
ObserverAPI observer1 = new ConcreteObserver(concreteSubject);
// 也可以單獨給主題注冊一個新的觀察者
concreteSubject.register(new ConcreteObserver2());
// 可以移除觀察者對象,可以打開注釋試下
// concreteSubject.remove(observer1);
// 主題開始發布新通知,各觀察者自動更新
concreteSubject.notify("hello, this is broadcast.");

Python代碼

觀察者接口

 
# ObserverAPI.py 觀察者抽象父類,定義一些公共方法
class ObserverAPI:
def __init__(self, name):
self.name = name
# 觀察者發出更新通知,觀察者自行監聽
def update(self, content):
print(self.__class__.__name__ + '::update() [content = ' + content + ']')
def set_name(self, name):
self.name = name

具體觀察者

 
# ConcreteObserver.py 具體的觀察者實現類,也可以看成訂閱者,關聯對應的主題類。
# 不同的觀察者也可以對應多個主題
from src.ObserverAPI import ObserverAPI
# 具體的觀察者實現類,也可以看成訂閱者,關聯對應的主題類。
# 不同的觀察者也可以對應多個主題
class ConcreteObserver(ObserverAPI):
# 給觀察者綁定主題,同時把觀察者添加到主題列表
def __init__(self, subject, name):
ObserverAPI.__init__(self, name)
# python3支持的父類調用
# super(ConcreteObserver, self).__init__(name)
# super().__init__(name)
self.subject = subject
subject.register(self)
# 觀察者發出更新通知,不用單獨告訴訂閱者,由訂閱者自行監聽
def update(self, content):
print(self.__class__.__name__ + '::update() [subject.name = ' +
self.subject.name + ' content = ' + content + ']')

 
# ConcreteObserver2.py 具體的觀察者實現類,也可以看成訂閱者,關聯對應的主題類。
# 不同的觀察者可以對應不同的主題。
from src.ObserverAPI import ObserverAPI
# 具體的觀察者實現類,也可以看成訂閱者,關聯對應的主題類。
# 不同的觀察者可以對應不同的主題。
class ConcreteObserver2(ObserverAPI):
# 這里沒有在構造器就綁定某個主題,而是從客戶角度去注冊觀察者
# 觀察者發出更新通知,觀察者自行監聽
# def update(self, content):
# print(self.__class__.__name__ + '::update() [content = ' + content +']')
pass

抽象主題類

 
# Subject.py 定義抽象主題類或者接口,供具體主題類繼承
class Subject:
def __init__(self, name):
self.name = name
self.observers = []
def get_name(self):
return self.name
def set_name(self, name):
self.name = name
def register(self, observer):
print(self.__class__.__name__ + '::register() [observer = ' +
observer.__class__.__name__ + ']')
self.observers.append(observer)
def remove(self, observer):
self.observers.remove(observer)
# 通知由具體類來實現邏輯
def notify(self, name):
pass

具體主題類

 
// ConcreteSubject.py 觀察者主題類,也是發布者,重寫具體的通知方法。不同主題可以關聯不同的觀察者。
from src.Subject import Subject
# 觀察者主題類,也是發布者,重寫具體的通知方法。不同主題可以關聯不同的觀察者。
class ConcreteSubject(Subject):
# 不同的主題類有自己的通知方法,批量通知綁定的觀察者
def notify(self, content):
print(self.__class__.__name__ + '::notify() [content = ' + content +
']')
for observer in self.observers:
observer.update(content)

測試調用

 
import sys
import os
os_path = os.getcwd()
sys.path.append(os_path)
from src.ConcreteSubject import ConcreteSubject
from src.ConcreteObserver import ConcreteObserver
from src.ConcreteObserver2 import ConcreteObserver2
def test():
'''
* 觀察者模式應用非常廣泛,主要是觀察者提前綁定到發布者
* 當發布者發布消息時,批量廣播通知,而無需逐一通知
* 觀察者監聽到消息后自己決定采取哪一種行為
'''
# 定義一個主題,也就是發布者
concrete_subject = ConcreteSubject('subject1')
# 再聲明觀察者,通過構造器注冊到主題上
observer1 = ConcreteObserver(concrete_subject, 'observer1')
# 也可以單獨給主題注冊一個新的觀察者
observer2 = ConcreteObserver2('observer2')
concrete_subject.register(observer2)
# 可以移除觀察者對象
# concrete_subject.remove(observer1)
# 主題開始發布新通知,各觀察者自動更新
concrete_subject.notify('hello, this is broadcast.')
if __name__ == '__main__':
print(__file__)
print("test start:")
test()

更多語言版本

不同語言實現設計模式:https://github.com/microwind/design-pattern

??

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

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

相關文章

PCI 總線樹BUS 號

在一個處理器系統中&#xff0c;每一個host 主橋都推出一棵PCI 總線樹。 一棵PCI 總線樹中有多少個PCIB bridge , 就含有多少條PCI 總線。 系統軟件在遍歷當前PCI 總線樹時&#xff0c;需要首先對這些PCI 總線進行編號&#xff0c;即初始化PCI 橋的primary, secondary 和Subord…

C++自學: virtual function

使用virtual關鍵字在base class中聲明virtual function。你可以使用指針在derived class中調用和執行virtual function。 “virtual void MakeSound() 0;”這是一個pure virtual function。 有至少一個pure virtual function的class稱為abstract class&#xff0c;所以Instr…

策略梯度方法

策略梯度方法 數學背景 給定一個標量函數 J ( θ ) J\left(\theta\right) J(θ)&#xff0c;利用梯度上升法&#xff0c;使其最大化&#xff0c;此時的 π θ \pi_\theta πθ?就是最優策略。 θ t 1 θ t α ? θ J ( θ t ) \theta_{t1}\theta_t\alpha \nabla_\theta…

Element Plus el-table 數據為空時自定義內容【默認為 No Data】

1. 通過 Table 屬性設置 <div class"el-plus-table"><el-table empty-text"暫無數據" :data"tableData" style"width: 100%"><el-table-column prop"date" label"Date" width"180" /&g…

系統架構設計師---OSI七層協議

目錄 OSI七層協議 各層主要功能和詳細說明 Internet協議的主要協議及其層次關系

常用的請求 Content-Type 類型

application/json&#xff1a;用于發送 JSON 格式的數據&#xff0c;常用于 RESTful API 請求中。 application/x-www-form-urlencoded&#xff1a;在 POST 請求中以 URL 編碼(key-value 對)的方式發送表單數據。 multipart/form-data&#xff1a;用于同時上傳文件和其他表單…

vue + el-table 表格數據導出為excel表格

下載依賴 npm install --save xlsx file-saver引入插件 import * as XLSX from xlsx; import FileSaver from "file-saver";完整代碼 <template><div class"administrativeCase-container"><div class"content-box"><di…

Java后端實現不用pagehelper。手寫分頁如何實現?

Java后端實現不用pagehelper。手寫分頁如何實現? 如果你不使用PageHelper這樣的分頁插件&#xff0c;你可以手動實現分頁邏輯。下面是一個使用Java后端手寫分頁的示例&#xff1a; 首先&#xff0c;確定每頁顯示的數據量和當前頁碼。 int pageSize 10; // 每頁顯示的數據量…

移動通信系統的LMS自適應波束成形技術matlab仿真

目錄 1.算法運行效果圖預覽 2.算法運行軟件版本 3.部分核心程序 4.算法理論概述 5.算法完整程序工程 1.算法運行效果圖預覽 2.算法運行軟件版本 matlab2022a 3.部分核心程序 ..................................................................... idxx0; while idxx&…

Spring Bean的生命周期總結(包含面試題)

目錄 一、Bean的初始化過程 1. 加載Spring Bean 2. 解析Bean的定義 3. Bean屬性定義 4. BeanFactoryPostProcessor 擴展接口 5. 實例化Bean對象 6. Aware感知 7. 初始化方法 8. 后置處理 9. destroy 銷毀 二、Bean的單例與多例模式 2.1 單例模式&#xff08;Sin…

游戲不再只是娛樂,更成為了一種學習和成長的途徑

隨著科技的飛速發展和游戲設計的創新&#xff0c;當下的游戲行業正經歷著前所未有的繁榮時代。各種各樣的游戲類型在不斷涌現&#xff0c;為玩家們帶來了豐富多彩的娛樂體驗。這些火熱的游戲類型不僅改變了我們的娛樂方式&#xff0c;還在無形中影響了我們的生活。 多元游戲類…

2023河南萌新聯賽第(六)場:河南理工大學-F 愛睡大覺的小C

2023河南萌新聯賽第&#xff08;六&#xff09;場&#xff1a;河南理工大學-F 愛睡大覺的小C https://ac.nowcoder.com/acm/contest/63602/F 文章目錄 2023河南萌新聯賽第&#xff08;六&#xff09;場&#xff1a;河南理工大學-F 愛睡大覺的小C題意解題思路 題意 新學期的概…

大數據平臺中元數據庫—MySQL的異常故障解決

本文的主要目標是解決大數據平臺中元數據庫MySQL的異常故障。通過分析應用響應緩慢的問題&#xff0c;找到了集群組件HIVE和元數據庫MySQL的原因。通過日志分析、工具檢測和專家指導等一系列方法&#xff0c; 最終確定問題的根源是大數據集群中租戶的不規范使用所導致&#xff…

[Unity]Lua本地時間、倒計時和正計時。

慣例&#xff0c;直接上代碼&#xff1a; --正計時開始時的時間戳 self.begin_time os.time() --倒計時時長&#xff0c;01:30:00 self.countdown_time 5400 --是否開始計時 self.is_update_local_time true--Unity Update function time_transition:update_local_timer()i…

Linux學習之iptables過濾規則的使用

cat /etc/redhat-release看到操作系統是CentOS Linux release 7.6.1810&#xff0c;uname -r看到內核版本是3.10.0-957.el7.x86_64&#xff0c;iptables --version可以看到iptables版本是v1.4.21。 iptables -t filter -A INPUT -s 10.0.0.8 -j ACCEPT會在最后一行插入。 10…

代碼隨想錄day52

300最長遞增子序列 class Solution { public:int lengthOfLIS(vector<int>& nums) {int piles 0; // 牌堆數初始化為 0vector<int> top(nums.size()); // 牌堆數組 topfor (int i 0; i < nums.size(); i) {int poker nums[i]; int left 0, right…

04 qt功能類、對話框類和文件操作

一 QT中時間和日期 時間 ---- QTime日期 ---- QDate對于Qt而言,在實際的開發過程中, 1)開發者可能知道所要使用的類 ---- >幫助手冊 —>索引 -->直接輸入類名進行查找 2)開發者可能不知道所要使用的類,只知道開發需求文檔 ----> 幫助 手冊,按下圖操作: 1 …

Android 13像Settings一樣獲取SIM卡信息

一.背景 由于客戶定制的Settings里面需要獲取到SIM卡信息,所以需要實現此功能。 目錄 一.背景 二.前提條件 三.調用api 二.前提條件 首先應用肯定要是系統應用,并且導入framework.jar包,具體可以參考: Android 應用自動開啟輔助(無障礙)功能并使用輔助(無障礙)功能_…

python中的cnn:介紹和基本使用方法

python中的cnn&#xff1a;介紹和基本使用方法 卷積神經網絡&#xff08;Convolutional Neural Networks&#xff0c;簡稱CNN&#xff09;是一種在圖像識別、語音識別、自然語言處理等許多領域取得顯著成功的深度學習模型。CNN的設計靈感來源于生物的視覺系統&#xff0c;由多…

WordPress更換域名后-后臺無法進入,網站模版錯亂,css失效,網頁中圖片不顯示。完整解決方案(含寶塔設置)

我在實際解決問題時用到了 【簡單暴力解決方案】的《方法一&#xff1a;修改wp-config.php》 和 【簡單暴力-且特別粗暴-的解決方案】 更換域名時經常遇到的幾個問題&#xff1a; 1、更換域名后&#xff0c;后臺無法進入 2、更換域名后&#xff0c;網站模版錯亂&#xff0c;c…