設計模式之觀察者模式(二)

来源:https://www.cnblogs.com/dimple91/archive/2019/03/29/10606393.html
-Advertisement-
Play Games

上一篇的觀察者模式學習的還好嗎?首先簡單來回顧下上篇內容,有一個氣象站的需求,需要在溫度、濕度、氣壓改變的時候,實時更新三個佈告板,以便能及時、準確的獲取信息。所以,在設計模式的層面,我們最容易想到並且最正確的方式就是使用觀察者模式來處理這個問題。 上一篇,我們通過一系列的分析,並畫出符合要求的類圖 ...


上一篇的觀察者模式學習的還好嗎?首先簡單來回顧下上篇內容,有一個氣象站的需求,需要在溫度、濕度、氣壓改變的時候,實時更新三個佈告板,以便能及時、準確的獲取信息。所以,在設計模式的層面,我們最容易想到並且最正確的方式就是使用觀察者模式來處理這個問題。

上一篇,我們通過一系列的分析,並畫出符合要求的類圖,然後使用了第一種方式,通過自己動手寫設計模式實現了觀察者模式的功能。但是,截止到目前為止,我們觀察者模式的方式都是由被觀察者主動發送狀態通知給觀察者,從而達到實時更新。這樣有個弊端就是被觀察者不能事先知道觀察者每個人的需求,有些觀察者其實只需要一點點數據,但無奈會受到一堆數據。有沒有方式,讓觀察者通過getter方法,主動獲取新增的狀態呢。答案是,有。Java內置的Observer模式兩種方法都支持。

java.util包內包含基本的Observer介面與Observable類,這和我們的Subject介面與Observer介面很相似。Observer介面與Observable類使用上更方便。因為許多功能都事先準備好了。你甚至可以使用推(PUSH)或拉(PULL)的方式傳送數據。下麵就看看,我們修改後的氣象站OO設計。
image

Java內置的觀察者模式如何運作

Java內置的觀察者模式運作方式,和我們在氣象站中的實現類似,但有一點小差異。最明顯的差異是WeatherData現在擴展自Obserable類,並繼承到一些增加、刪除、通知觀察者的方法(以及其他的方法)。Java版本的用法如下:

如何把對象變成觀察者

如同以前一樣,實現觀察者介面(java.util.Observer),然後調用任何Observable對象的 addObserver()方法。不想再當觀察者時,調用deleteObserver()方法即可。

可觀察者要如何送出通知

首先,你需要利用擴展java.util.Observable介面產生"可觀察者"類 ,然後,需要兩個步驟:

  1. 先調用setChanged()方法,標記狀態已經改變的事實
  2. 然後調用兩種notifyObservers()方法中的一個:notifyObservers()或notifyObservers(Object arg)

觀察者如何接收通知

同以前一樣,觀察者實現了更新的方法,但是方法的簽名不太一樣:
update(Observable o, Object arg)
o:主題本身當做第一個變數,好讓觀察者知道是哪個主題通知它的
arg:這正是傳入notifyObservers()的數據對象。如果沒有說明,則為空。
如果你想推(push)數據給觀察者,你可以把數據當做數據對象傳給notifyObservers(arg0)方法。否則,觀察者就必須從可觀察者對象中拉(pull)數據,如何拉數據,不急不急,我們很快和大家見面。

利用內置的支持重做氣象站

首先,把WeatherData改成使用java.util.Observable

package com.dimple.headfirst.observer;

// 1:記得要導入(import)正確的Observer/Observable
import java.util.Observable;

/**
 * 
 * @Title:
 * @Description: 2:我們現在正繼承Observable
 * 3:我們不需要追蹤觀察者了,也不需要管理註冊與刪除(讓超類代勞即可)。
 * 我們把註冊、添加、通知的相關代碼刪除
 */
public class WeatherDataNew extends Observable {
    private float temperature;
    private float humidity;
    private float pressure;
    
    // 4:我們的構造器不再需要為了記住觀察者們而建立數據結構了
    public WeatherDataNew() {
        
    }
    
    public void measurementsChanged() {
        // 5: 調用notifyObservers()之前,要先調用setChanged()來指示狀態已經改變
        setChanged();
        // 註意:我們沒有調用notifyObservers()傳送數據對象,這表示我們採用的做法是拉
        notifyObservers();
    }
    
    public void setMeasurements(float temperature, float humidity,float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        measurementsChanged();
    }
    
    // 6:這些並不是新方法,只是因為我們要使用“拉”的做法,所以才提醒你有這些方法。觀察者會利用這些方法取得WeatherData對象的狀態
    public float getTemperature() {
        return temperature;
    }
    
    public float getHumidity() {
        return humidity;
    }
    
    public float getPressure() {
        return pressure;
    }
}

現在,讓我們重做CurrentConditionsDisplay

package com.dimple.headfirst.observer;

import java.util.Observable;
// 1:導入正確的Observer/Observable
import java.util.Observer;

/**
 * 
 * @Title:
 * @Description: 2:我們正在實現java.util.Observer介面
 */
public class CurrentConditionDisplayNew implements Observer, DisplayElement {
    
    Observable observable;
    private float temperature;
    private float humidity;
    
    // 3:構造器需要 Observable當參數,並將 CurrentConditionsDisplay對象登記成觀察者
    public CurrentConditionDisplayNew(Observable observable) {
        this.observable = observable;
        observable.addObserver(this);
    }

    // 4:改變update方法,增加Observable和數據對象作為參數
    @Override
    public void update(Observable obs, Object arg) {
        // 5:在update()中,先確定可觀察者屬於WeatherData類型,然後利用getter方法獲取溫度和濕度測量值,最後調用display() 
        if (obs instanceof WeatherDataNew) {
            WeatherDataNew weatherDataNew = (WeatherDataNew)obs;
            this.temperature = weatherDataNew.getTemperature();
            this.humidity = weatherDataNew.getHumidity();
            display();
        }
    }

    // 顯示溫度和濕度
    @Override
    public void display() {
        System.out.println("Current conditions: " + temperature + " F degrees and " + humidity + "% humidity");
    }
}

運行新的代碼

public class WeatherStation {
    public static void main(String[] args) {
        WeatherDataNew weatherDataNew = new WeatherDataNew();
        CurrentConditionDisplayNew currentConditionDisplayNew = new CurrentConditionDisplayNew(weatherDataNew);
        weatherDataNew.setMeasurements(80, 65, 30.4f);
    }
}

// 結果顯示
Current conditions: 80.0 F degrees and 65.0% humidity

至此,我們就把Java內置的觀察者模式學習了一遍,你學會了嗎?

但是,不知道你發現沒有,內置的觀察者模式中,可觀察者是一個“類”而不是一個“介面”,甚至沒有實現一個介面,這樣就限制了他的使用和復用,所以還是需要做下強調,提醒大家這是一個“類”的事實。

Observable是一個類

首先,因為Observable是一個“類”,你必須設計一個類繼承它。如果某類想同事具有Observable類和另一個超類的行為,就會有糾結,畢竟Java只支持單繼承嘛。

Observable將關鍵的方法保護起來

 protected synchronized void setChanged() {
        changed = true;
    }

setChanged()方法是一個保護方法,這就意味著除非你繼承自Observable,否則你無法創建Observable實例並組合到你自己的對象中來。這個設計也就違反了之前第二個設計原則“多用組合,少用繼承”。

做什麼呢?

如果你能夠擴展java.util.Obserable,那麼Observable“可能”可以符合你的需求。否則,你可能需要像我們上一篇那樣,自己去實現一套觀察者模式,不過不管怎樣,相信你都已經掌握了觀察者模式,這已經難不倒你了。

因為觀察者模式是平時使用比較廣泛,並且大家會頻繁的接觸甚至是使用,所以我本來想在這篇進行總結的,考慮到篇幅的原因,讓這個總結放到下一篇,通過這兩個篇幅的學習和實踐,把自己動手寫觀察者模式以及使用Java內置的方式來使用觀察者模式都進行了掌握。其他語言的不知道有沒有內置的,只能說Java程式員在看完這篇文章之後,肯定又多了一種選擇,給自己鼓掌吧。

PS:代碼已經上傳,需要查看的朋友點擊此處HeadFirstDesign

愛生活,愛學習,愛感悟,愛挨踢

image


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • radio組件 v-model : 通過當然綁定的值與input上的value值來確定當前選中項。 在父作用域中通過active設置當前預設選中項,如果選中項發生改變後通過input事件通知傳遞到父作用域,告知當前選中項.(自定義組件 v-model 數據雙向綁定) radioGroup組件 把ra ...
  • Friendly-errors-webpack-plugin 介紹 Friendly-errors-webpack-plugin識別某些類別的webpack錯誤,並清理,聚合和優先順序,以提供更好的開發人員體驗。 我們運行nodejs 可以看到錯誤信息如下: 安裝入門 npm install frie ...
  • 1.轉化為 年月日 2.轉化為 年月日 時分秒 ...
  • 在本次Webpack 4教程中,我們會更進一步講述項目優化。我們會學習什麼是tree shaking以及如何使用它。你會找到讓Webpack 4中tree shaking運作起來所需要的東西,並知道怎樣從中受益。開始吧! ...
  • ComboBox listeners:{ expand:function(){ //此函數是,點擊下拉框展開的時候事件 }, select:function(com, record, index){ //下拉框選擇事件 //比如二級聯動 }, change:function(this,newValu... ...
  • Math.random():獲取0~1隨機數Math.floor() method rounds a number DOWNWARDS to the nearest integer, and returns the result. (小於等於 x,且與 x 最接近的整數。)其實返回值就是該數的整數位 ...
  • 背景介紹jQuery中有個toggle()方法,可以切換元素的顯示狀態,在用vue時如果不引入jQuery如何實現這種效果?使用場景常見有三種場景:1.控制頁面中一個dom元素2.控制頁面中多個dom元素3.控制頁面中的dom元素是從服務端返回,且有多個技術分析1.一二場景下實現這種效果可以用v-i ...
  • DI越來越重要 DI就是依賴註入,現在來說,大部分框架都是以DI為基礎組件的,每一個框架都有自己的DI組件,像dotnet core,java spring等,也都為自己的框架量身打造了DI工具。 面向對象的幾個原則 依賴倒置原則(DIP):一種軟體架構設計的原則(抽象概念)。 控制反轉(IoC): ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...