觀察者模式 Observer 發佈訂閱模式 源 監聽 行為型 設計模式(二十三)

来源:https://www.cnblogs.com/noteless/archive/2018/12/20/10147516.html
-Advertisement-
Play Games

觀察者模式 Observer,又被稱為發佈訂閱模式或者源監聽,是一種行為型設計模式,也是也是很多系統中常常用到的一種消息處理機制,Java內置了對觀察者模式的支持,藉助於觀察者模式可以很好地完成消息的發佈與訂閱,本文對觀察者模式進行了簡介,並且給出了意圖和結構的解析,並且給出了Java代碼示例。 ...


觀察者模式 Observer image_5c1af1b9_5671

意圖

定義對象一種一對多的依賴關係,當一個對象的狀態發生改變時,所有依賴他的對象都得到通知並自動更新。 別名:依賴(Dependents),發佈訂閱(Publish-Subscribe)源-監聽(Source-Listener)   image_5c1af1b9_431a 《Hold On, We're Going Home》是加拿大說唱歌手德雷克與製作組合Majid Jordan合作的節奏布魯斯歌曲 第一句“I got my eyes on you”就是“我一直關註你”   I got my eyes on you, You're everything that I see I want your hot love and emotion, endlessly I can't get over you, You left your mark on me......   電視劇中,也是經常出現”觀察、監視“,比如《黎明之前》 是劉江執導2010年出品的諜戰劇,由吳秀波、林永健、陸劍民、海清等領銜主演。豆瓣評分高達9.2。 有一段周漢亭被盯梢的橋段,有負責門口監視的,有負責電話彙報情況的,有負責指揮的....他的一舉一動都在敵人的監視之內,引發無數個探子連鎖行動。   歌詞中因為喜歡妹子所以持續關註,妹子是目標,歌手是觀察者。 電視劇中敵人為了破壞我黨工作,所以監視,周漢亭是目標,眾多探子是觀察者。 通過對目標的觀察,觀察者可以獲得事物的動向情況,進而做出進一步的行動。這就是觀察。   在程式中,也經常會出現這種場景 一個系統中必然由多個相互協作的類組成,很常見的問題就是維持狀態的一致性或者說聯動效果 觀察者模式就是為瞭解決這種問題,處理“協作者”之間的一致性與聯動問題   比如,資料庫中對某個欄位進行了更新,可能需要同步修改其他欄位   比如,執行一個main方法,IDE的視窗的聯動效果,如下圖所示,點擊run執行後 底部狀態欄會顯示Build狀態,很快完成後就開始運行,側邊欄顯示運行狀態,然後控制台列印輸出信息 這是一系列的聯動效果 image_5c1af1b9_6655 比如,同一份數據有多種圖表展示,如果數據發生變化,每個圖表都需要發生變化 image_5c1af1b9_1bae

結構

假設目標為Subject ,觀察者為Observer(一個或者多個) 最簡單的實現方式,就是Subject直接調用Observer的方法。 image_5c1af1b9_4e72 當事件發生後,直接調用Observer的相關方法。 偽代碼如下 Subject內使用List維護觀察者 當事件發生,也就是方法f()中,迴圈通知觀察者 省略了觀察者的維護工作,也就是添加和刪除
class Subject{
List<Observer> observerList = new ArrayList<>();
void f(){
//do sth...
for(Observer o:observerList){
//調用相關方法
o.doSthElse();
}
}

 

依賴倒置原則中,要求應該面向抽象進行編程,而不是面向細節 上面的結構中,不管是目標還是觀察者的擴展都不方便,所以抽象提取。 image_5c1af1b9_6dec   這就是觀察者模式的基本結構。 抽象觀察者角色Observer 為所有的具體的觀察者定義一個介面,得到主題的通知信息後,進行同步響應。 一般包含一個方法叫做update()用以同步響應 抽象主題角色Subject 主題角色把所有觀察者對象保存在集合中,提供管理工作,添加和刪除 並且,提供通知工作,也就是調用相關觀察者的update 具體主題角色ConcreteSubject 實現抽象主題介面協議,當狀態方式發生變化時,對觀察者進行通知 具體觀察者角色ConcreteObserver 實現抽象觀察者定義的介面,完成自身相關的同步更新活動

代碼示例

抽象觀察者角色,提供統一的更新方法
package observer;
public interface Observer {
void update();
}
抽象的Subject,內部使用List<Observer>保存觀察者對象 提供了attach和dettach方法用於添加和刪除觀察者 並且提供了通知方法,就是遍歷調用Observer
package observer;
import java.util.LinkedList;
import java.util.List;
 
public abstract class Subject {
 
    List<Observer> observerList;
     
    Subject() {
        observerList = new LinkedList<>();
    }
     
    void attach(Observer o) {
        observerList.add(o);
    }
     
    void dettach(Observer o) {
        observerList.remove(o);
    }
     
    void notifyObservers() {
        for (Observer o : observerList) {
            o.update();
        }
    }
}
具體的主題角色,他有一個行動方法,行動後通知其他的觀察者 觀察者在父類中列表裡面定義
package observer;
public class ConcreteSubject extends Subject {
    public void action() {
        System.out.println("下雨了");
        super.notifyObservers();
    }
}
具體的觀察者,實現具體的行動
package observer;
public class ConcreateObserver implements Observer {
    @Override
    public void update() {
        System.out.println("那我收衣服了");
    }
}
測試代碼
package observer;
public class Test {
public static void main(String[] args) {
        Observer o1 = new ConcreateObserver();
        ConcreteSubject subject = new ConcreteSubject();
        subject.attach(o1);
        subject.action();
    }
}

 

image_5c1af1b9_179e 如果新增加一個觀察者
package observer;
public class ConcreteObserver1 implements Observer {
    @Override
    public void update() {
        System.out.println("那我得把窗戶關上");
    }
}

 

測試代碼 image_5c1af1ba_3e32 如上圖所示,有兩個觀察者(比如張三和李四),當包租婆大喊一聲下雨了之後,他們都得到通知 張三去收衣服了,李四把自己的窗戶關上了   以上就是觀察者模式的簡單示例。   觀察者模式的核心在於對於觀察者的管理和維護,以及發送通知。 消息的發佈訂閱,在程式中就是消息發佈者調用訂閱者的相關方法 觀察者模式將發佈者與訂閱者進行解耦,不再是直接的方法調用,通過引入Observer角色,完成了發佈者與具體訂閱者之間的解耦 也是一種形式的“方法調用”的解耦

Java的觀察者模式

image_5c1af1ba_413f   觀察者介面Observer
是一個interface,定義了一個update方法
void update(Observable o, Object arg);
接受兩個參數,一個是被觀察對象,一個是參數
被觀察角色Observerable 等同於前文Subject 內部使用Vector維護觀察者Observer 提供了對觀察者的管理相關方法,添加、刪除、計算個數、刪除、刪除所有等   Observerable內部使用標誌位記錄是否已經發生變化     private boolean changed = false;   發生變動後,設置為true,通知後設置為false image_5c1af1ba_7fcf  
addObserver(Observer o) 添加指定觀察者
deleteObserver(Observer o)  刪除指定觀察者
deleteObservers()      刪除所有觀察者
countObservers 返回觀察者個數
notifyObservers() notifyObservers(Object arg)  通知所有觀察者 一個無參,一個有參 參數傳遞給Observer的update
  Observerable的實現原理很簡單:
  • 使用Vector保存觀察者,提供了添加、刪除、刪除全部的方法,並且可以返回觀察者的個數。
  • 如果的確發生變動,將會通知所有的觀察者
提供了兩個版本的notifyObservers,一個有參,有個無參,參數對應update方法的第二個參數 image_5c1af1ba_6594   通知後,將會清除變動狀態   image_5c1af1ba_7878 Observable中Vector<Observer>是私有的,也並沒有提供訪問器,只是可以添加、刪除、或者清除所有 所以子類並不知道具體的Observer image_5c1af1ba_38a4 代碼示例
package observer.java;
import java.util.Observable;
public class Subject extends Observable {
    public void changeState() {
        System.out.println("下雨了........");
        setChanged();
        notifyObservers();
    }
}

 

package observer.java;
import java.util.Observable;
import java.util.Observer;
public class Watcher1 implements Observer {
    @Override
    public void update(Observable o, Object arg) {
        System.out.println("被觀察者:" + o + " ,參數 " + arg + " ,觀察者1");
    }
}

 

 

package observer.java;
import java.util.Observable;
import java.util.Observer;
public class Watcher2 implements Observer {
    @Override
    public void update(Observable o, Object arg) {
        System.out.println("被觀察者:" + o + " ,參數 " + arg + " ,觀察者2");
    }
}
image_5c1af1ba_7d48   Subject 繼承Observable類,自定義了changeState()方法 在方法中,調用     setChanged();     notifyObservers(); 完成狀態改變和通知。   從列印信息可以看得出來 update方法接收到的第一個參數,就是我們的被觀察者對象 第二個參數可以用來封裝傳遞信息   所以在java中,除非場景特殊,你又不需要自己寫觀察者模式了,已經內置了 通過繼承和實現相應的類和介面即可。   GOF的設計模式出版於95,JDK 1.0始於1996,所以,Java天然支持某些設計模式也很正常 而且,設計模式是經驗總結,GOF將他們歸納總結使之廣為人知,但是並不代表這些經驗史無前例 JDK的開發者人家本身就有這些“經驗”也不足為奇。

與中介者模式區別

觀察者模式用於一對多依賴場景中的解耦,通過引入Observer角色,將消息發佈者與具體的訂閱者進行解耦 中介者模式是將系統內部多對多的複雜耦合關係,藉助於中介者進行解耦,將網狀結構,簡化為星型結構,將多對多轉換為一對多    他們都能夠實現消息發佈者與接收者的解耦,消息發佈者都不知道具體的消息接收者 發起消息的Colleague同事類角色是被觀察者,中介類Mediator是觀察者,調用其他的同事類進行協作   儘管觀察者模式強調“一致性通信” 中介者模式強調“內部組件協作” 但是根本還是方法執行時,需要同步調用其他對象   兩個模式之間是一種類似的關係,在有些場景可替代轉換。 如果協作關係比較簡單,可以實現為一對多的形式,使用觀察者模式 如果協作關係更加複雜,那麼就可以使用中介者模式

總結

觀察者模式是在一對多的依賴場景中,對消息發佈者和消息訂閱者的解耦 在觀察者和被觀察者之間建立了一個抽象的耦合,而不是強關聯 通過引入觀察者角色,發佈者不依賴具體的觀察者,從而Subject和Observer可以獨立發展 被觀察者僅僅知道有N個觀察者,他們以集合的形式被管理,都是Observer角色,但是完全不知道具體的觀察者   觀察者模式的支持廣播,被觀察者會向所有的觀察者發送消息。   增加新的觀察者時,不需要修改客戶端代碼,只需要擴展Observer介面即可,滿足開閉原則   一個觀察者,也可能是一個被觀察者,如果出現觀察者調用鏈,將會發生多米諾骨牌效應不斷地進行調用,後果可能是難以預知的。 所以要謹慎識別雙重身份的對象,也就是即是觀察者也是被觀察者的對象。   被觀察者不知道具體的觀察者,也更不可能知道觀察者具體的行為 當發生狀態變化時,被觀察者一個小舉動,可能引起很大的性能消耗,而被觀察者對此毫不知情,可能仍舊以為雲淡風輕。   當一個對象狀態的改變,需要同時改變其他對象時,可以考慮觀察者模式 當一個對象必須通知其他人時,但是他又不知道到底是誰時,可以考慮觀察者模式 或者將一個抽象模型中的兩個關聯部分解耦,以便獨立發展,提高復用性,解耦不表示斷開,仍舊需要聯繫,可以藉助於觀察者模式進行聯繫 總之 觀察模式通過引入觀察者角色,將調用者與被調用者解耦,通過觀察者角色聯繫。 但凡類似“廣播”“發佈訂閱”的場景,都可以考慮是否可用。 原文地址:觀察者模式 Observer 行為型 設計模式(二十三)  
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv ...
  • 楔子 最近一個項目,需要繪製雙線的效果,雙線效果表示的是軌道(類似鐵軌之類的),如下圖所示: 負責這塊功能開發的小伙,姑且稱之為L吧,最開始是通過數學計算的方式來實現這種雙線,也就是在原來的路徑的基礎上,計算出兩條路徑。但是這個過程的計算算挺複雜,而是最終實現的效果很耗性能,性能損耗估計主要在於路徑 ...
  • 一、概念 模板方法模式:在一個方法中定義一個演算法的骨架,而將一些步驟延遲到子類中。模板方法使得子類可以在不改變演算法結構的情況下,重新定義演算法中的某些步驟。 解析:模板方法模式用來創建一個演算法的模板。什麼是模板?模板就是一個方法。更具體地說,這個方法將演算法定義成一組步驟,其中的任何步驟都可以是抽象的, ...
  • 在一次公開課上,聽別人講過全局分散式uuid的設計,聽過twitter的snowflake的設計。也聽過,如果使用單獨的計數器服務,不可能每次都保存當前計數器到文本,自己想到應該可以每隔一些數,例如1萬次,10萬次,反正64位的空間比較大,然後保存起來,那麼就沒有每次保存,對硬碟的寫入壓力。當出現故 ...
  • 建造者模式 一:建造者概述 我們大家可能都會開小汽車,但是當你得到一輛小汽車的時候,你可以用它馳騁馬路,但是你知道它組件複雜的構造過程嗎,並不知道。而我們今天要講的建造者模式其實就是 ,`客戶端無需知道複雜對象的內部組成和裝配方式,只需要知道建造者的類型即可 一步步的創建獨立的複雜對象,不同的具體構 ...
  • PHP註冊樹模式主要用於創建對象的時候將我們的對象與相應的變數進行綁定,從這個角度上說,Yii2的Service Locator和DI Container都用到註冊樹模式。這二者都在內部維護一個數組(key => value),value為對象或者對象定義,在獲取時通過唯一的key來獲取,如果是定義... ...
  • 一、面向過程 我們是怎麼思考和解決上面的問題的呢? 答案是:我們自己的思維一直按照步驟來處理這個問題,這是我們常規思維,這就是所謂的面向過程POP編程 二、面向過程POP為什麼轉換為面向對象OOP 面向過程想的思想步驟越多,變化越多,是無法掌控的,所以有時候非常複雜,就比如我們拿起來手機玩游戲如果按 ...
  • 作者按:《每天一個設計模式》旨在初步領會設計模式的精髓,目前採用 和`python`兩種語言實現。誠然,每種設計模式都有多種實現方式,但此小冊只記錄最直截了當的實現方式 :) 個人技術博客 "godbmw.com" 歡迎來玩! 每周至少 1 篇原創技術分享,還有開源教程(webpack、設計模式)、 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...