設計模式之觀察者模式

来源:http://www.cnblogs.com/zpfbuaa/archive/2016/05/15/5496286.html
-Advertisement-
Play Games

觀察者模式:在對象之間定義一對多的依賴,這樣一來,當一個對象改變狀態,依賴它的對象都會收到通知,並自動更新。 舉個例子:氣象監測應用 此系統的三個部分是氣象站(獲取實際氣象數據的物理裝置)、WeatherData對象(追蹤來自氣象站的數據,並更新佈告板)和佈告板(有三個佈告板,一個用來顯示目前天氣狀 ...


觀察者模式:在對象之間定義一對多的依賴,這樣一來,當一個對象改變狀態,依賴它的對象都會收到通知,並自動更新。

  舉個例子:氣象監測應用

    此系統的三個部分是氣象站(獲取實際氣象數據的物理裝置)、WeatherData對象(追蹤來自氣象站的數據,並更新佈告板)和佈告板(有三個佈告板,一個用來顯示目前天氣狀況,一個顯示氣象統計,最後一個用來顯示天氣預報)

  看一下WeatherData源文件:

  

  上面的三個get各自返回最近的氣象測量數據(分別為:溫度、濕度、氣壓),我們不在乎這些變數如何通過氣象站這個物理裝置得到以及它們如何設置,WeatherData對象自己知道如何從氣象站獲取更新信息。

  measurementsChanged() {/* 一旦氣象測量更新,此方法會調用,這裡面寫自己的代碼 */}

  先看一個錯誤示範:

  

public class WeatherData{
    
    //實例變數聲明

    public void measurementsChanged()
    {
        //調用get函數,獲取最近的測量值
        float temp = getTemperature();
        float humidity = getHumidity();
        float pressure = getPressure();
        //更新現在佈告板、氣象統計佈告板、天氣預報佈告板
        currentConditionsDisplay.update(temp,humidity,pressure);
        statisticsDisplay.update(temp,humidity,pressure);
        forecastDisplay.update(temp,humidity,pressure);
    }
    //這裡是其他的WeatherData方法
}

 

  問題出在哪裡?為什麼這樣是錯誤的?

 

currentConditionsDisplay.update(temp,humidity,pressure);
statisticsDisplay.update(temp,humidity,pressure);
forecastDisplay.update(temp,humidity,pressure);
/*
以上屬於具體實現編程,會導致我們以後在增加或刪除佈告板時必須修改程式*/

/*
另外上面的代碼屬於改變的部分,因此需要封裝獨立出來
*/

   在繼續向下走之前,先看一個觀察者模式的實例

  

  尋找工作的軟體開發人員將自己的聯繫方式留給獵頭,然後當獵頭獲取工作應聘消息時需要通知他手裡名單上的所有人員。

  但是也有這樣的情況,當軟體開發人員沒有向獵頭提交信息時,獵頭在收到消息後不同通知他。

  

  同樣接受獵頭通知的求職人員同樣可以取消通過獵頭來尋求工作,比如軟體開發人員2取消了這種求職方式,那麼他就不會再收到獵頭的通知。

  

  但是軟體開發人員5希望通過這種方式來找工作,那麼就需要去和獵頭聯繫並留下自己的聯繫方式,那麼從此軟體開發人員5也可以收到通知了

  

  在明白上面的例子之後,重新回到我們的氣象監測應用上。

  嘗試著畫出實現氣象站所需要的類,其中包括WeatherData類以及佈告欄組件。確定你的圖像能夠顯示出各個部分如何結合起來,以及別的開發人員如何能夠實現自己的佈告欄組件。(每個佈告欄都應該有一個被命名為“subject(主題)”的指針指向WeatherData對象,為了避免混亂下麵沒有畫出)

  

  實現氣象站:

   從建立介面開始

public interface Subject{
    public void registerObserver(Observer o);
    public void removeObserver(Observer o);
    public void notifyObservers();
}

public interface Observer{
    public void update(float temp,float humidity,float pressure);
}

public interface DisplayElement{
    public void display();
}

 

  在WeatherData中實現主題介面

public class WeatherData implements Subject{//WeatherData實現了Subject介面
    private ArrayList observers;//添加一個ArrayList來記錄觀察者,此ArrayList是在構造器中建立的
    private float temperature;
    private float humidity;
    private float pressure;
    
    public WeatherData(){
        observers = new ArrayList();
    }
    
    public void registerObserver(Observer o){//當註冊觀察者時,我們只要把它加到ArrayList後面即可
        observers.add(o);
    }
    
    public void removeObserver(Observer o){//同樣刪除時,得到索引然後從ArrayList刪除即可
        int i = observers.indexOf(o);
        if(i >= 0){
            observers.remove(i);
        }
    }
    
    public void notifyObservers(){//這裡我們把狀態都告訴每一個觀察者,因為觀察者都實現了update(),
        for(int i = 0 ;i < observers.size(); i++){
            Observer observer = (Observer)observers.get(i);
            observer.update(temperature,humidity,pressure);
        }
    }
    
    public void measurementsChanged(){//當氣象站更新數據時,我們通知觀察者
        notifyObservers();
    }
    
    public void setMeasurements(float temperature,float humidity,float pressure){
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        measurementsChanged();
    }
}

下麵只給出了目前狀況的佈告板:

  

//此佈告板實現了Observer介面,所以可以從WeatherData對象中獲取改變,同樣也實現了DisplayElement的介面
public class CurrentConditionsDisplay implements Observer,DisplayElement{
    private float temperature;
    private float humidity;
    private Subject weatherData;
    
    public CurrentConditionsDisplay(Subject weatherData){//構造器需要weatherData對象 作為註冊使用
        this.weatherData = weatherData;
        weatherData.registerObserver(this);
    }
    
    public void update(float temperature,float humidity,float pressure){
        this.temperature = temperature;
        this.humidity = humidity;//當update()調用時,我們把溫度和濕度保存起來,然後調用display() 
        display();//這裡之前說的可能不清楚,目前狀況只需要顯示溫度和濕度就可以了,
                //氣象檢測系統不要求在這個佈告板顯示氣壓
    }
    
    public void display(){
        System.out.println("Current conditions:"+temperature
            +"F degrees and "+ humidity +" % humidity");
    }
}

 

 

  下麵討論使用Java內置的觀察者模式來完成氣象監測應用

 在java.util包中包含最基本的Observer介面與Observable類。甚至可以實現推(push)和拉(pull)方式來傳送數據。什麼叫做push,什麼叫做pull呢?

  還用之前的例子,獵頭主動將信息通知給每一位軟體開發人員叫做push,那麼至於拉(pull)就很直觀了,就是軟體開發人員主動向獵頭聯繫索取信息。(每個佈告欄都應該有一個被命名為“subject(主題)”的指針指向WeatherData對象,為了避免混亂下麵沒有畫出)

  

  在這裡需要註意的地方:

  在Observable裡面有一個setChanged()方法,它的作用是在調用notifyObserver()之前先調用setChanged(),如果changed為true那麼就進行通知,否則就不通知。(setChanged()在每次消息有所改變時都要進行修改

  看一下裡面的代碼:

  

setChanged(){
    changed= true;//將changed標誌設為true
}
notifyObservers(Object arg){
    if(changed){//只有在是true是才通知
        for every observer on the list{
            call update(this.arg);
        }
        changed=false;//通知完觀察者之後,把changed標誌設回false
    }
}

notifyObservers(){
    notifyObservers(null);
}

 

  1.Observable是一個類,首先既然它是個類,那麼我們需要設計一個類來繼承它。但是Java不支持多重繼承,這就限制了Observable的服用能力。在再者因為沒有Observable介面,所以自己無法創建自己的實現來和Java內置的Observer API搭配使用,也無法將java.util的實現換成另一套做法的實現。

  2.Observable將關鍵的方法保護起來,如果查看Observable API則會發現,setChanged()方法被保護了起來(被定義為protected)。這就意味著:除非你繼承自Observable,否則你無法創建Observable實例並組合到你自己的對象中來。這違背了設計原則“多用組合,少用繼承”

  到此我們現在的設計模式已經學習了三個——簡單工廠模式、策略模式以及現在的觀察者模式。

  未來的路還很遠,加油吶!

 


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

-Advertisement-
Play Games
更多相關文章
  • 模板方法模式,定義一個操作中的演算法的骨架,而將一些步驟延遲到子類中。模板方法使得子類可以不改變一個演算法的結構即可重新定義該演算法的某些特定步驟。 模板方法模式是通過把不變行為搬到超類,去除子類中的重覆代碼來體現它的優勢。 當不變和可變的行為在方法的子類實現中混合在一起的時候,不變的行為就會在子類中重覆 ...
  • 原型模式其實就是從一個對象再創建另外一個可定製的對象,而且不需要知道任何創建的細節。 .NET在System命名空間中提供了ICloneable介面,其中就是唯一的一個方法Clone(),這樣你就只需要實現這個介面就可以完成原型模式。(選至《大話設計模式》) MemberwiseClone()方法, ...
  • 摘要:本文介紹了簡單工廠模式的概念,優缺點,實現方式,以及結合Annotation和反射的改良方案(讓簡單工廠模式不簡單)。同時介紹了簡單工廠模式(未)遵循的OOP原則。最後給出了簡單工廠模式在JDBC中的應用 原創文章。同步自作者個人博客 "http://www.jasongj.com/desig ...
  • "設計模式系列目錄" 新博客 "wossoneri.com" 單一職責原則 Single Responsibility Principle SRP 就一個類而言,應該僅有一個引起它變化的原因。 假設現在要在iPhone上做一個圖片編輯工具。功能有裁剪圖片,旋轉圖片,縮放移動照片等等。 吶,我們可以寫 ...
  • 利用繼承來提供對象的行為,會導致: 1、代碼在多個子類中重覆 2、很難知道所有鴨子的全部行為 3、運行時的行為不易改變 4、改變會牽一發而動全身,造成其他鴨子不想要的改變 設計原則 1 :找出應用之中可能需要變化之處,把他們獨立出來,不要和那些不需要變化的代碼混在一起。 註釋:把會變化的部分取出並“ ...
  • (原文地址:http://www.cnblogs.com/hellohuang/p/5492302.html ) 這是一個簡單實現有分步式框架,由5個服務進程組成一個伺服器,它們分別是世界服(Ws),資料庫處理服(Dp),場景服(Ss),網關服(Fep),框架的思想用來自工作項目框架(但沒有它的代碼 ...
  • 前段時間因為“某度競價排名”的事情鬧得沸沸揚揚的,因為某度搜出來的內容的質量實在是不敢恭維,所以早就戒掉了某度改用Google了。但是Google早就被牆了呢,所以翻牆才能訪問Google呢,這個“翻牆”的過程就是一個代理的過程。“代理模式”在之前的博客中不止一次的提及過,之前的委托回調就是代理模式 ...
  • 一、簡介Dubbo是Alibaba開源的分散式服務框架,它最 大的特點是按照分層的方式來架構,使用這種方式可以使各個層之間解耦合(或者最大限度地 松耦合)。從服務模型的角度來看,Dubbo採用的是一種非常簡單的模型,要麼是提供方提供服務,要麼是消費方消費服務,所以基於這一點可以抽象出服務提 供方(P ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...