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

来源: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
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...