Java觀察者模式實現方式與測試方法

一、實現方式

  1. 自定義實現
    通過手動定義SubjectObserver接口,實現一對多依賴關系:
// 觀察者接口
public interface Observer {void update(float temp, float humidity, float pressure);
}
// 主題接口
public interface Subject {void registerObserver(Observer o);void removeObserver(Observer o);void notifyObservers();
}
// 具體主題類(天氣數據)
public class WeatherData implements Subject {private List observers = new ArrayList<>();private float temperature, humidity, pressure;@Overridepublic void registerObserver(Observer o) {observers.add(o);}@Overridepublic void removeObserver(Observer o) {observers.remove(o);}@Overridepublic void notifyObservers() {for (Observer o : observers) {o.update(temperature, humidity, pressure);}}public void setMeasurements(float temp, float hum, float press) {temperature = temp;humidity = hum;pressure = press;notifyObservers();}
}

特點:靈活可控,但需自行處理線程安全和內存管理。


  1. 使用JDK內置類(ObservableObserver
    JDK提供的Observable類和Observer接口簡化實現,但需注意其設計缺陷(如需手動調用setChanged()):
// 具體主題類
public class WeatherData extends Observable {private float temperature, humidity, pressure;public void setMeasurements(float temp, float hum, float press) {temperature = temp;humidity = hum;pressure = press;setChanged();  // 標記狀態變化notifyObservers(new MeasurementData(temp, hum, press)); // 推模型}
}
// 具體觀察者類
public class CurrentConditionsDisplay implements Observer {@Overridepublic void update(Observable o, Object arg) {if (arg instanceof MeasurementData) {MeasurementData data = (MeasurementData) arg;System.out.println("溫度:" + data.getTemp());}}
}

注意事項:

  • notifyObservers()需在setChanged()后調用,否則不觸發通知。
  • 觀察者需通過參數arg獲取數據(推模型)或從主題拉取數據(拉模型)。

  1. 高級實現(異步與線程安全)
    通過線程池異步通知觀察者,避免阻塞主題線程:
public class AsyncSubject extends Subject {private ExecutorService executor = Executors.newFixedThreadPool(4);@Overridepublic void notifyObservers() {for (Observer o : observers) {executor.submit(() -> o.update(...));}}
}

優勢:提升并發性能,避免單線程阻塞。


二、測試方法

  1. 單元測試(Mockito框架)
    使用Mockito模擬觀察者行為,驗證通知邏輯:
@Test
public void testObserverNotification() {// 1. 創建Mock觀察者Observer mockObserver = Mockito.mock(Observer.class);// 2. 注冊觀察者到主題WeatherData weatherData = new WeatherData();weatherData.registerObserver(mockObserver);// 3. 觸發狀態變化weatherData.setMeasurements(25.5f, 65, 1013.1f);// 4. 驗證觀察者方法是否被調用Mockito.verify(mockObserver).update(25.5f, 65, 1013.1f);
}

關鍵點:

  • 使用when()設置Mock對象行為(如異常拋出)。
  • 使用verify()驗證方法調用次數和參數。

  1. 集成測試(多線程場景)
    測試觀察者模式在并發環境下的穩定性:
@Test
public void testConcurrentObservers() {WeatherData weatherData = new WeatherData();Observer observer1 = new CurrentConditionsDisplay();Observer observer2 = new StatisticsDisplay();// 多線程注冊觀察者Thread t1 = new Thread(() -> weatherData.registerObserver(observer1));Thread t2 = new Thread(() -> weatherData.registerObserver(observer2));t1.start();t2.start();// 等待線程結束t1.join();t2.join();// 觸發通知并驗證weatherData.setMeasurements(30f, 70, 1012.5f);// 驗證兩個觀察者均被通知
}

注意事項:

  • 使用synchronized或并發集合(如CopyOnWriteArrayList)保證線程安全。
  • 測試觀察者是否因循環依賴導致死鎖。

  1. 性能測試
    評估觀察者數量對通知效率的影響:
@Test
public void testObserverPerformance() {WeatherData weatherData = new WeatherData();int observerCount = 1000;for (int i = 0; i < observerCount; i++) {weatherData.registerObserver(new SimpleObserver());}long startTime = System.currentTimeMillis();weatherData.setMeasurements(25f, 60, 1013f);long endTime = System.currentTimeMillis();// 驗證耗時是否在合理范圍內Assert.assertTrue(endTime - startTime < 1000); // 1秒內完成
}

優化方向:

  • 使用異步通知減少阻塞。
  • 限制觀察者數量或引入優先級隊列。

三、常見問題與解決方案

  1. 內存泄漏:
    • 問題:未從主題移除觀察者,導致無法被GC回收。
    • 解決方案:在觀察者銷毀時調用removeObserver(),或使用弱引用存儲觀察者。
  2. 循環依賴:
    • 問題:主題與觀察者互相依賴,導致棧溢出。
    • 解決方案:通過中介者模式解耦。
  3. 線程安全:
    • 問題:多線程環境下注冊/移除觀察者時數據不一致。
    • 解決方案:使用synchronized或并發集合(如CopyOnWriteArrayList)。

四、總結

  • 實現選擇:優先自定義實現以避免JDK類的缺陷,復雜場景可結合異步通知。
  • 測試重點:驗證通知邏輯、線程安全及性能。
  • 工具推薦:Mockito用于單元測試,線程池和并發集合用于多線程場景。

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

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

相關文章

leetGPU解題筆記(1)

1.題面 題目要求 向量加法 實現一個程序&#xff0c;在GPU上對兩個包含32位浮點數的向量執行逐元素加法。該程序應接受兩個長度相等的輸入向量&#xff0c;并生成一個包含它們和的輸出向量。 實現要求 禁止使用外部庫 solve函數簽名必須保持不變 最終結果必須存儲在向量C中 示例…

5. JVM 的方法區

1. JVM介紹和運行流程-CSDN博客 2. 什么是程序計數器-CSDN博客 3. java 堆和 JVM 內存結構-CSDN博客 4. 虛擬機棧-CSDN博客 5. JVM 的方法區-CSDN博客 6. JVM直接內存-CSDN博客 7. JVM類加載器與雙親委派模型-CSDN博客 8. JVM類裝載的執行過程-CSDN博客 9. JVM垃圾回收…

網絡安全的基本練習

一.docker搭建 1.安裝dockerapt-get install docker.io docker-compose2.編寫配置文件&#xff08;注意路徑正確&#xff09;vim /etc/systemd/system/docker.service.d/http-proxy.conf[Service] Environment"HTTP_PROXYhttp://科學上網訪問的ip:端口" Environment&…

380. O(1) 時間插入、刪除和獲取隨機元素

實現RandomizedSet 類&#xff1a; RandomizedSet() 初始化 RandomizedSet 對象 bool insert(int val) 當元素 val 不存在時&#xff0c;向集合中插入該項&#xff0c;并返回 true &#xff1b;否則&#xff0c;返回 false 。 bool remove(int val) 當元素 val 存在時&#xff…

【LeetCode Hot100 | 每日刷題】字母異位詞分組

題目鏈接&#xff1a;49. 字母異位詞分組 - 力扣&#xff08;LeetCode&#xff09; 題目&#xff1a; 給你一個字符串數組&#xff0c;請你將 字母異位詞 組合在一起。可以按任意順序返回結果列表。 示例 1: 輸入: strs ["eat", "tea", "tan"…

docker 安裝windows

目錄 下載地址&#xff1a; 使用教程&#xff1a; docker compose 查看版本 測試啟動 hello-world 報錯1 The system cannot find the file specified&#xff1a; 檢查 Docker Desktop 是否運行中 報錯2HF_ENDPOINT 1. 臨時解決方案&#xff08;當前終端會話有效&…

docker compose 和build

目錄 docker compose 和build 的區別是什么&#xff1f; 核心差別&#xff1a; 1. docker build --platform linux/amd64 -f Dockerfile -t infiniflow/ragflow:nightly_lbg . 2. docker compose -f docker-compose-gpu.yml up -d 二者如何配合&#xff1f; 總結 docker …

裂變時刻:全球關稅重構下的券商交易系統躍遷路線圖(2025-2027)

——基于RWA清算、量子加密與實時非線性風控的下一代跨境基礎設施核心事件錨定&#xff1a;特朗普于7月7日對14國啟動分級關稅制裁&#xff08;日韓25%、東南亞30%-40%、金磚關聯國10%附加稅&#xff09;&#xff0c;引發日元兌美元暴跌至144.47、銅價單日跳漲3.2%、散戶單日交…

python爬蟲初入門——基本庫和寫入方法

1.準備環境 python環境&#xff1a;3.10 2.常用庫 1.請求庫&#xff1a;實現 HTTP 請求操作 requests&#xff1a;基于 urllib 編寫的&#xff0c;阻塞式 HTTP 請求庫&#xff0c;發出一個請求&#xff0c;一直等待服務器響應后&#xff0c;程序才能進行下一步處理。seleni…

Sonar掃描C#代碼配置

需要的工具 MSBuild、sonar-scanner-4.6.1.2450-windows、jdk1.8.0_181 下載地址&#xff1a;https://download.csdn.net/download/code12313/91315686 配置sonar的地址 一、環境變量配置 1.新建變量&#xff0c;nameSONAR_RUNNER_MSBUILD_HOME。valueD:\work\dev\dev_serve…

python 在運行時沒有加載修改后的版本

陳舊的Python字節碼 (.pyc 文件)&#xff1a;最常見的原因&#xff01;Python 會把你修改的 .py 文件編譯成 .pyc 字節碼來加速后續運行。有時&#xff0c;即使你修改了 .py 文件&#xff0c;系統可能仍然固執地加載舊的、未被刪除的 .pyc 文件。1. 用“硬編碼探針”強制驗證# …

【會員專享數據】2013-2024年我國省市縣三級逐年SO?數值數據(Shp/Excel格式)

之前我們分享過2013-2024年全國范圍逐年SO?柵格數據&#xff08;可查看之前的文章獲悉詳情&#xff09;&#xff01;該數據來源于韋晶博士、李占清教授團隊發布在國家青藏高原科學數據中心網站上的中國高分辨率高質量近地表空氣污染物數據集。很多小伙伴拿到數據后反饋柵格數據…

出現SSL連接錯誤的原因和解決方案

介紹 SSL連接錯誤是一種常見但關鍵的問題&#xff0c;這可能會阻止客戶端和服務器之間的安全連接。這些錯誤發生在TLS握手過程失敗時&#xff0c;這意味著客戶端和服務器無法建立安全的HTTPS連接。這種失敗可以在SSL/TLS協商過程中的任何階段發生&#xff0c;從初始協議協議到…

vue3 el-date-picker 保存后 日期減一問題

在使用 el-date-picker&#xff08;Element UI 的日期選擇器組件&#xff09;時&#xff0c;如果你發現日期在保存到后臺后自動減一&#xff0c;這通常是由于時區差異或者是時間格式解析問題導致的。這里有一些可能的解決方案&#xff1a;1. 檢查前端發送的日期格式確保你在前端…

什么是IP關聯?跨境賣家如何有效避免IP關聯?

一位深圳賣家曾管理30個亞馬遜店鋪賬號&#xff0c;某日清晨發現所有賬號被批量封禁——原因竟是平臺檢測到這些賬號長期共享同一IP地址&#xff0c;判定為“IP關聯”。而在跨境領域如亞馬遜、eBay、Shopee、TikTok等平臺&#xff09;&#xff0c;對于IP關聯的判定都是比較嚴格…

Redis集群方案——哨兵機制

Redis Sentinel&#xff08;哨兵&#xff09;是Redis官方提供的高可用性(HA)解決方案&#xff0c;用于管理Redis主從架構并實現自動故障轉移。一、集群結構和作用哨兵是一個分布式系統&#xff0c;由多個哨兵節點組成&#xff1a;哨兵的作用如下&#xff1a;監控&#xff1a;Se…

1.2.3_2 TCP/IP模型

在這個視頻中&#xff0c;我們要探討TCP/IP模型。對于TCP/IP模型考試的要求是這樣的&#xff0c;首先我們需要記住它各個層次的名稱和順序&#xff0c;以及我們需要了解TCP/IP 模型和OSI參考模型&#xff0c;它們在設計理念上有哪些區別&#xff0c;設計理念的區別又導致了TCP/…

EVOLVEpro安裝使用教程-蛋白質語言模型驅動的快速定向進化

寫在前面&#xff1a;蛋白質是生命活動的基石&#xff0c;其功能和序列之間的復雜關系長期以來吸引著科學家們的關注。盡管深度突變掃描等實驗方法可以解析蛋白質突變的功能影響&#xff0c;但這些技術的應用范圍局限于序列空間的一小部分。近年來&#xff0c;基于蛋白質語言模…

【Linux】Rocky Linux 清華鏡像源安裝 GitLab

使用清華鏡像源安裝 GitLab 地址&#xff1a;清華鏡像源 1. 搜索 gitlab&#xff0c;我們選擇 gitlab-ce 社區版進行安裝 2. 新建 /etc/yum.repos.d/gitlab-ce.repo&#xff0c;內容為 注意&#xff1a;el$releasever 是清華鏡像源內的文件夾版本 [gitlab-ce] nameGitlab C…

【龍澤科技】新能源汽車維護與動力蓄電池檢測仿真教學軟件【吉利幾何G6】

產品簡介新能源汽車維護與動力蓄電池檢測仿真教學軟件是依托《全國職業院校技能大賽》“新能源汽車維修”賽項中“新能源汽車維護與動力蓄電池檢測” 競賽模塊&#xff0c;自主開發的一款仿真教學軟件。軟件采用仿真技術對車輛進行指定維護作業&#xff0c;并對動力蓄電池總成進…