2.Observer Pattern(觀察者模式)

来源:http://www.cnblogs.com/lanshanxiao/archive/2017/12/06/7978885.html
-Advertisement-
Play Games

Observer Pattern(觀察者模式)定義: 在對象之間定義一對多的依賴,這樣一來,當一個對象改變狀態,依賴它的對象都會收到通知,並自動更新。 乾說定義肯定沒有舉例理解的透徹。想到Observer Pattern(觀察者模式)就來舉個生活中的例子來幫助我們更好消化和理解其具體含義。 舉例: ...


Observer Pattern(觀察者模式)定義:

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

  乾說定義肯定沒有舉例理解的透徹。想到Observer Pattern(觀察者模式)就來舉個生活中的例子來幫助我們更好消化和理解其具體含義。

舉例:  

  訂閱雜誌或者報紙,這裡面有兩個主角,一個是報紙雜誌的供應商(報社),一個是報紙雜誌的訂閱者。這裡就是被觀察者也叫主題(供應商)和觀察者(訂閱者)。主題(Subject)應該有觀察者名單,當主題有新的報紙售出時將按主體持有的觀察者名單一個一個發送新報紙(發送沒有先後順序,一切按存儲順序發送)。

  同時主題還應該有三個方法:

    一、將觀察者寫入名單中(registerObserver())

    二、將觀察者從名單中刪除(removeObserver())

    三、當有新消息發送及時通知名單中所有觀察者(notifyObservers())

 1 public interface Subject {//主題介面,所有報社都要實現該介面
 2     
 3     /*沒有存儲訂閱者的列表,是因為我們不想在介面中寫死存儲方式,
 4       讓編程人員自己在實現介面的時候寫入想要的存儲方式(如:鏈表,數組,棧,隊列等)
 5       這樣更合理。*/
 6     
 7     public void registerObserver(Observer o);//將訂閱者登記在列表中
 8     public void removeObserver(Observer o);//將訂閱者從列表中移除
 9     public void notifyObservers(Object arg);//有參通知方法,有新的消息即使通知列表中所有訂閱者
10     public void notifyObservers();//無參通知方法
11 }
Subject

  觀察者所具有的東西就會少一些:

    首先,內部需要有存儲主題的對象,這樣知道觀察者所訂閱的報社是哪一家,具有主題對象還有一個重要的原因,把登記、刪除、通知觀察者的功能全部委托給主題去做。

    其次,還需要有更新自己消息的方法(update())新的消息發送過來,觀察者也要及時更新自己內部消息,將舊的消息替換成新的消息。

1 public interface Observer{//所有訂閱者要實現的介面
2     /*在介面中,沒有主題對象,也是因為不想將主題對象寫死在介面中,
3     在具體類中寫入更好*/
4     
5     public void update(Subject sub, Object args);//接收到新消息,及時更新
6 }
Observer

現在,我們來以具體的生活例子來介紹如何實現觀察者模式(Observer Pattern):

  有一家氣象站,氣象站本身已經具有WeatherData對象(相當於報社功能,可以獲得目前的溫度、濕度、氣壓三種數據)。

  我們需要編寫一個應用,該應用有很多種顯示模式(從溫度、濕度、氣壓中任選一到三個組合就是一種模式)。

  當WeatherData對象獲得最新的測量數據時,我們的應用可以及時更新顯示模式中的數據。

根據要求寫程式:

  主題介面:

 1 public interface Subject {//主題介面,所有報社都要實現該介面
 2     
 3     /*沒有存儲訂閱者的列表,是因為我們不想在介面中寫死存儲方式,
 4       讓編程人員自己在實現介面的時候寫入想要的存儲方式(如:鏈表,數組,棧,隊列等)
 5       這樣更合理。*/
 6     
 7     public void registerObserver(Observer o);//將訂閱者登記在列表中
 8     public void removeObserver(Observer o);//將訂閱者從列表中移除
 9     public void notifyObservers(Object arg);//有參通知方法,有新的消息即使通知列表中所有訂閱者
10     public void notifyObservers();//無參通知方法
11 }
Subject

  主題介面實體類:

 1 import java.util.ArrayList;
 2 
 3 public class WeatherData implements Subject{//氣象站的實現類
 4     private ArrayList observers;//觀察者列表
 5     private float temperature;//數據之一:溫度
 6     private float humidity;//數據之二:濕度
 7     private float pressure;//數據之三:氣壓
 8     private boolean status;//數據是否更新的標誌
 9     
10     public WeatherData(){//初始化時,為觀察者列表賦值
11         observers = new ArrayList();
12     }
13     
14     public float getTemperature(){//獲取溫度的方法
15         return this.temperature;
16     }
17     
18     public float getHumidity(){//獲取濕度的方法
19         return this.humidity;
20     }
21     
22     public float getPressure(){//獲取氣壓的方法
23         return this.pressure;
24     }
25     
26     public void registerObserver(Observer o){//將觀察者記錄在列表中
27         observers.add(o);
28     }
29     
30     public void removeObserver(Observer o){//將觀察者從列表中刪除
31         int i = observers.indexOf(o);
32         observers.remove(i);
33     }
34     
35     public void notifyObservers(Object args){//有參通知觀察者方法
36         if(status){//判斷數據是否有更新
37             for(int i = 0; i < observers.size(); i++){
38                 Observer observer = (Observer) observers.get(i);
39                 observer.update(this, args);
40             }
41             status = false;//消息發送成功後,將更新標誌位重置
42         }
43     }
44     
45     public void notifyObservers(){//無參通知觀察者方法
46         notifyObservers(null);
47     }
48     
49     public void setChange(){//數據是否更新的標誌
50         status = true;
51     }
52     
53     public void setMeasurements(float temperature, float humidity, float pressure){//數據更新方法
54         this.temperature = temperature;
55         this.humidity = humidity;
56         this.pressure = pressure;
57         measurementsChanged();
58     }
59     
60     public void measurementsChanged(){//數據改變後調用該方法
61         setChange();
62         notifyObservers();
63     }
64     
65     
66 }
WeatherData

  觀察者介面:

1 public interface Observer{//所有訂閱者要實現的介面
2     /*在介面中,沒有主題對象,也是因為不想將主題對象寫死在介面中,
3     在具體類中寫入更好*/
4     
5     public void update(Subject sub, Object args);//接收到新消息,及時更新
6 }
Observer

  顯示更新數據的介面:

1 public interface DisplayElement{//顯示更新數據的介面
2     public void display();
3 }
DisplayElement

  觀察者介面實體類:

 1 public class CurrentConditionsDisplay implements Observer, DisplayElement{//顯示當前溫度、濕度的類
 2     private WeatherData weatherData;//定義訂閱的主題對象
 3     private float temperature;//溫度數據
 4     private float humidity;//濕度數據
 5     
 6     public CurrentConditionsDisplay(WeatherData weatherData){
 7         this.weatherData = weatherData;
 8         weatherData.registerObserver(this);//將該觀察者對象登記在主題的觀察者列表中
 9     }
10     
11     public void update(Subject sub, Object args){//數據更新方法
12         if(sub instanceof WeatherData){
13             WeatherData weatherData = (WeatherData) sub;
14             this.temperature = weatherData.getTemperature();
15             this.humidity = weatherData.getHumidity();
16             display();
17         }
18     }
19     
20     public void display(){//顯示數據的方法
21         System.out.println("Current conditions:" + temperature + "F degrees and " + humidity + "%humidity");
22     }
23 }
CurrentConditionsDisplay

  測試類:

 1 public class WeatherStation{
 2     public static void main(String[] agrs){
 3         WeatherData weatherData = new WeatherData();
 4         
 5         CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);
 6         
 7         weatherData.setMeasurements(80, 65, 30.4f);//更新數據
 8         weatherData.setMeasurements(82, 70, 29.2f);//更新數據
 9         weatherData.setMeasurements(79, 90, 29.2f);//更新數據
10     }
11 }
WeatherStation

 編譯運行結果:

上面代碼已經很完善了,而且不知道你有沒有發現,其實每次WeatherData更新數據都是把所有數據都更新,但是我們的CurrentConditionsDispaly只獲取溫度和濕度兩個數據,並且從來不獲取多餘的氣壓數據。這就是數據推送(Push)和數據抽取(Pull)的區別。

Push:不管你有沒有訂閱該數據,主題都會將該數據發送給訂閱者,再由訂閱者決定數據的取捨,沒用的數據就不會記錄在自己的數據中。

Pull:訂閱者想要什麼數據由訂閱者說了算,主題只需提供獲取數據的方法(get...())就好,而上面我們的氣象站就是使用了數據抽取方式。

思想提煉:

  1.多用組合,少用繼承

  2.為交互對象之間的松耦合設計而努力

  


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

-Advertisement-
Play Games
更多相關文章
  • " " 在學習的過程中會發現很多知識點如果不在工作中運用或者手寫帶驗證的話,很容易忘記。任何技能的掌握都是需要不斷練習的。在此收集一些自己遇到的JavaScript練習的網站。 "codewars" 國外的一個練習網站,有JavaScript,也有Python,每種語言都有自己的道場(kata),每 ...
  • react-bootstrap是一個非常受歡迎的針對react封裝過的bootstrap,它本身不包含css,所以也是需要使用bootstrap原生庫。 在create-react-app建的項目目錄中安裝react-bootstrap。 安裝bootstrap。 在index.js文件中增加css ...
  • 之前做過一個項目,其中一項功能是查看社交信息流帖子。很多帖子中都包含視頻,手機上播放視頻後,會有層級混亂的問題。 當時的解決方案是動態的將視頻放入Iframe中。以後如果有其他的解決方式會追加進來。 視頻使用的是16:9的比例。 示例: HTML: CSS: JS: ...
  • ECMA script(發音為“ek-ma-script”)和 javaScript 的關係以及和瀏覽器的關係 雖然JavaScript和ECMAscript通常被人們用來表達相同的含義,但javacript的含義卻比ECMA-script多很多。一個完整的JavaScript實現應該由下列三個不用 ...
  • 採用MUI開發APP時,頁面跳轉傳值無疑是很多初學者遇到的難題之一,我在開發時也遇到了同樣的問題,所以在這裡總結了一下,方便以後查閱。 一、頁面預載入時傳值 通過上述方法預載入頁面,然後在載入的那個頁面中接受參數。 二、通過mui.openWindow打開視窗向頁面傳遞參數 這種傳值方法通常我們的做 ...
  • 今天看到一個kata,提出一個“emirps”的概念:一個質數倒轉後得到的是一個不同的質數,這個數叫做“emirps”。 例如:13,17是質數,31,71也是質數,13和17是“emirps”。 但是質數757,787,797是迴文質數,這意味著反轉的數字與原始數字相同,所以它們不被認為是“emi ...
  • 說明:這裡主要介紹jQuery的分頁插件twbsPagination。當然了還有其他的分頁插件,感覺上這個插件還是比較簡單易用的。 步驟一:建立page.jsp頁面,引用jquery.twbsPagination.js,page.js <script src="<%=basePath%>js/jqu ...
  • 裝飾者模式: 動態地將責任附加到對象上。想要擴展功能,裝飾者提供有別於繼承的另一種選擇。 舉例: 不知道大家學校的食堂是什麼點餐制度(或者大家就直接想成吃火鍋,我們要火鍋料 + 配菜),我們學校的點餐是:主食大米 + 你想要吃的菜(每個菜都裝在小碗中)。現在問題來了,我點的是大米(0.8元) + 紅 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...