設計模式探索:觀察者模式

1. 觀察者模式

1.1 什么是觀察者模式

觀察者模式用于建立一種對象與對象之間的依賴關系,當一個對象發生改變時將自動通知其他對象,其他對象會相應地作出反應。
在這里插入圖片描述

在觀察者模式中有如下角色:

  • Subject(抽象主題/被觀察者): 抽象主題角色把所有觀察者對象保存在一個集合里,每個主題可以有任意數量的觀察者,抽象主題提供一個接口,可以增加和刪除觀察者對象。
  • ConcreteSubject(具體主題/具體被觀察者): 該角色將有關狀態存入具體觀察者對象,在具體主題的內部狀態發生改變時,給所有注冊過的觀察者發送通知。
  • Observer(抽象觀察者): 觀察者的抽象類,定義了一個更新接口,使得在得到主題更改通知時更新自己。
  • ConcreteObserver(具體觀察者): 實現抽象觀察者定義的更新接口,以便在得到主題更改通知時更新自身的狀態。在具體觀察者中維護一個指向具體目標對象的引用,存儲具體觀察者的有關狀態,這些狀態需要與具體目標保持一致。
1.2 觀察者模式實現
  • 觀察者
/*** 抽象觀察者*/
public interface Observer {// update方法: 為不同觀察者的更新(響應)行為定義相同的接口,不同的觀察者對該方法有不同的實現void update();
}/*** 具體觀察者*/
public class ConcreteObserverOne implements Observer {@Overridepublic void update() {// 獲取消息通知,執行業務代碼System.out.println("ConcreteObserverOne 得到通知!");}
}/*** 具體觀察者*/
public class ConcreteObserverTwo implements Observer {@Overridepublic void update() {// 獲取消息通知,執行業務代碼System.out.println("ConcreteObserverTwo 得到通知!");}
}
  • 被觀察者
/*** 抽象目標類*/
public interface Subject {void attach(Observer observer);void detach(Observer observer);void notifyObservers();
}/*** 具體目標類*/
public class ConcreteSubject implements Subject {// 定義集合,存儲所有觀察者對象private ArrayList<Observer> observers = new ArrayList<>();// 注冊方法,向觀察者集合中增加一個觀察者@Overridepublic void attach(Observer observer) {observers.add(observer);}// 注銷方法,用于從觀察者集合中刪除一個觀察者@Overridepublic void detach(Observer observer) {observers.remove(observer);}// 通知方法@Overridepublic void notifyObservers() {// 遍歷觀察者集合,調用每一個觀察者的響應方法for (Observer obs : observers) {obs.update();}}
}
  • 測試類
public class Client {public static void main(String[] args) {// 創建目標類(被觀察者)ConcreteSubject subject = new ConcreteSubject();// 注冊觀察者類,可以注冊多個subject.attach(new ConcreteObserverOne());subject.attach(new ConcreteObserverTwo());// 具體主題的內部狀態發生改變時,給所有注冊過的觀察者發送通知。subject.notifyObservers();}
}

2. 發布訂閱模式與觀察者模式的區別

2.1 定義上的不同

發布訂閱模式屬于廣義上的觀察者模式。

  • 發布訂閱模式是最常用的一種觀察者模式的實現,從解耦和重用角度來看,更優于典型的觀察者模式。
2.2 兩者的區別

我們來看一下觀察者模式與發布訂閱模式結構上的區別
在這里插入圖片描述

操作流程上的區別

  • 觀察者模式:數據源直接通知訂閱者發生改變。
  • 發布訂閱模式:數據源告訴第三方(事件通道)發生了改變,第三方再通知訂閱者發生了改變。

3. 觀察者模式在實際開發中的應用

3.1 實際開發中的需求場景

在我們日常業務開發中,觀察者模式的一個重要作用在于實現業務的解耦。以用戶注冊的場景為例,假設在用戶注冊完成時,需要給該用戶發送郵件、發送優惠券等操作,如下圖所示:
在這里插入圖片描述

使用觀察者模式之后

在這里插入圖片描述

  • UserService 在完成自身的用戶注冊邏輯之后,僅需要發布一個 UserRegisterEvent 事件,而無需關注其它拓展邏輯。
  • 其它 Service 可以自己訂閱 UserRegisterEvent 事件,實現自定義的拓展邏輯。

3.2 Spring事件機制

Spring 基于觀察者模式,實現了自身的事件機制,由三部分組成:

在這里插入圖片描述

  • 事件 ApplicationEvent:通過繼承它,實現自定義事件。另外,通過它的 source 屬性可以獲取事件timestamp 屬性可以獲得發生時間。
  • 事件發布者 ApplicationEventPublisher:通過它,可以進行事件的發布。
  • 事件監聽器 ApplicationListener:通過實現它,進行指定類型的事件的監聽。
3.3 代碼實現

(1) UserRegisterEvent

  • 創建 UserRegisterEvent 事件類,繼承 ApplicationEvent 類,用戶注冊事件。代碼如下:
/*** 用戶注冊事件*/
public class UserRegisterEvent extends ApplicationEvent {private String username;public UserRegisterEvent(Object source) {super(source);}public UserRegisterEvent(Object source, String username) {super(source);this.username = username;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}
}

(2) UserService (事件源+事件發布)

  • 創建 UserService 類,代碼如下:
/*** 事件源角色+事件發布*/
@Service
public class UserService implements ApplicationEventPublisherAware {private Logger logger = LoggerFactory.getLogger(getClass());private ApplicationEventPublisher applicationEventPublisher;@Overridepublic void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {this.applicationEventPublisher = applicationEventPublisher;}public void register(String username){// 執行注冊邏輯logger.info("[register][執行用戶{}的注冊邏輯]", username);// 發布用戶注冊事件applicationEventPublisher.publishEvent(new UserRegisterEvent(this, username));}
}
  • 實現 ApplicationEventPublisherAware 接口,從而將 ApplicationEventPublisher 注入到其中。
  • 在執行完注冊邏輯后,調用 ApplicationEventPublisherpublishEvent(ApplicationEvent event) 方法,發布 UserRegisterEvent 事件。

(3) 創建 EmailService

/*** 事件監聽角色*/
@Service
public class EmailService implements ApplicationListener<UserRegisterEvent> {private Logger logger = LoggerFactory.getLogger(getClass());@Overridepublic void onApplicationEvent(UserRegisterEvent event) {logger.info("[onApplicationEvent][給用戶({}) 發送郵件]", event.getUsername());}
}
  • 實現 ApplicationListener 接口,通過 E 泛型設置感興趣的事件。
  • 實現 onApplicationEvent(E event) 方法,針對監聽的 UserRegisterEvent 事件,進行自定義處理。

(4) CouponService

@Service
public class CouponService {private Logger logger = LoggerFactory.getLogger(getClass());@EventListener public void addCoupon(UserRegisterEvent event) {logger.info("[addCoupon][給用戶({}) 發放優惠劵]", event.getUsername());}
}
  • 添加 @EventListener 注解,并設置監聽的事件為 UserRegisterEvent

(5) DemoController

  • 提供 /demo/register 注冊接口
@RestController
@RequestMapping("/demo")
public class DemoController {@Autowiredprivate UserService userService;@GetMapping("/register")public String register(String username) {userService.register(username);return "success";}
}
3.4 代碼測試
  1. 執行 DemoApplication 類,啟動項目。
  2. 調用 http://127.0.0.1:8080/demo/register?username=mashibing 接口,進行注冊。IDEA 控制臺打印日志如下:
// UserService 發布 UserRegisterEvent 事件
2023-04-19 16:49:40.628  INFO 9800 --- [nio-8080-exec-1] c.m.d.o.demo02.service.UserService       : [register][執行用戶mashibing的注冊邏輯]// EmailService 監聽處理該事件
2023-04-19 16:49:40.629  INFO 9800 --- [nio-8080-exec-1] c.m.d.o.demo02.listener.EmailService     : [onApplicationEvent][給用戶(mashibing) 發送郵件]// CouponService 監聽處理該事件
2023-04-19 16:49:40.629  INFO 9800 --- [nio-8080-exec-1] c.m.d.o.demo02.listener.CouponService    : [addCoupon][給用戶(mashibing) 發放優惠劵]

4. 觀察者模式總結

1) 觀察者模式的優點

  • 降低目標類和觀察者之間的耦合
  • 可以實現廣播機制

2) 觀察者模式的缺點

  • 通知的發送會消耗一定的時間
  • 如果被觀察者有循環依賴,會導致系統的崩潰

3) 觀察者模式常見的使用場景

  • 一個對象的改變,需要改變其他對象的時候
  • 一個對象的改變,需要進行通知的時候

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/44092.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/44092.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/44092.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【大模型】大規模部署LLM:挑戰與對策

大規模部署LLM&#xff1a;挑戰與對策 引言一、計算資源的挑戰1.1 計算成本1.2 能源消耗與碳足跡 二、維護與更新的挑戰2.1 模型更新與版本控制2.2 知識時效性 三、數據隱私與倫理考量3.1 數據隱私保護3.2 倫理與偏見 四、可擴展性與靈活性4.1 系統架構設計4.2 多語言與地域適應…

詳細分析@FunctionalInterface的基本知識(附Demo)

目錄 前言1. 基本知識2. Demo 前言 Java的基本知識推薦閱讀&#xff1a; java框架 零基礎從入門到精通的學習路線 附開源項目面經等&#xff08;超全&#xff09;Spring框架從入門到學精&#xff08;全&#xff09; 1. 基本知識 FunctionalInterface 是 Java 8 引入的一個注…

外賣商城平臺小程序的設計

管理員賬戶功能包括&#xff1a;系統首頁&#xff0c;個人中心&#xff0c;用戶管理&#xff0c;商家管理&#xff0c;騎手管理&#xff0c;商品類型管理&#xff0c;商品信息管理&#xff0c;訂單信息管理 微信端賬號功能包括&#xff1a;系統首頁&#xff0c;商品信息&#…

【AI資訊早報】AI科技前沿資訊概覽:2024年7月10日早報

AI科技前沿資訊概覽&#xff0c;涵蓋了行業大會、技術創新、應用場景、行業動態等多個方面&#xff0c;全面展現了AI領域的最新發展動態和未來趨勢。 一、人工智能大模型引領新業態 在2024年&#xff08;第二十三屆&#xff09;中國互聯網大會上&#xff0c;中國工程院院士鄔賀…

模板初階詳解

目錄 泛型編程函數模板函數模板概念函數模板格式函數模板的原理函數模板的實例化隱式實例化強制類型轉換的疑惑 顯式實例化 模板參數的匹配原則 類模板類模板的定義格式類模板的實例化 感謝各位大佬對我的支持,如果我的文章對你有用,歡迎點擊以下鏈接 &#x1f412;&#x1f41…

微信小程序接口wx.getLocation違規導致封禁解決辦法

1、找到站內信的這個封禁的通知&#xff08;功能封禁的通知&#xff0c;而不是處理警告的通知&#xff09; 2、點擊通知會有申訴鏈接&#xff0c;點開申訴鏈接 申訴原因可參考下面的內容&#xff1a; 1.小程序哪些板塊已除去收集地理位置、2.哪些板塊需要收集地理位置、3.詳細…

寶塔內 計劃任務更新遠程主機的時間

很多情況下一些主機無法上網,長此以往有可能讓系統內的時間混亂 ,這是一個很愁人的事情 這里我們找了一個可以通過寶塔的計劃任務或 cron 不斷將本地時間通過ssh登錄,并在登錄狀態下設置時間的方法.找了很多方案都不行 .最終采用了私鑰登錄的方案 1 使用寶塔的計劃任務(可選): …

WindowsMac共享文件夾設置

共享文件夾設置 共享文件夾設置Windows系統設置步驟一&#xff1a;設置共享文件夾步驟二: 訪問共享文件夾 Mac系統中設置共享文件夾步驟一&#xff1a;設置共享文件夾步驟二&#xff1a;訪問共享文件夾 小貼士結論 共享文件夾設置 有時需要在多臺電腦之間共享文件夾&#xff0…

4.MkDocs樣式

學習 Admonitions(警告) - Material for MkDocs (wdk-docs.github.io) 提示 - Material for MkDocs 中文文檔 (llango.com) Buttons(按鈕) - Material for MkDocs (wdk-docs.github.io) 建議去看這些網站&#xff0c;更為詳細。 常用功能 便利貼 ?? 開啟 markdown_ex…

Linux筆記之iftop查看特定IP地址吞吐量

Linux筆記之iftop查看特定IP地址吞吐量 code review! 文章目錄 Linux筆記之iftop查看特定IP地址吞吐量一.iftop安裝與監控二.iftop 界面簡單介紹如何查看單位實時流量的顯示形式控制單位顯示示例 三.數據存儲和傳輸的單位&#xff1a;比特&#xff08;bit&#xff09;和字節&…

Gemma2——Google 新開源大型語言模型完整應用指南

0.引言 Gemma 2以前代產品為基礎&#xff0c;提供增強的性能和效率&#xff0c;以及一系列創新功能&#xff0c;使其在研究和實際應用中都具有特別的吸引力。Gemma 2 的與眾不同之處在于&#xff0c;它能夠提供與更大的專有模型相當的性能&#xff0c;但其軟件包專為更廣泛的可…

hdfs大規模數據存儲底層原理詳解(第31天)

系列文章目錄 一、HDFS設計原理 二、HDFS系統架構 三、HDFS關鍵技術 四、HDFS應用實例 五、解決HDFS不能處理小文件詳解問題 文章目錄 系列文章目錄前言一、設計原理二、系統架構三、關鍵技術四、應用實例五、解決HDFS不能處理小文件詳解問題1. 合并小文件2. 優化Hive配置3. 使…

DDR3 SO-DIMM 內存條硬件總結(一)

最近在使用fpga讀寫DDR3&#xff0c;板子上的DDR3有兩種形式與fpga相連&#xff0c;一種是直接用ddr3內存顆粒&#xff0c;另一種是通過內存條的形式與fpga相連。這里我們正好記錄下和ddr3相關的知識&#xff0c;先從DDR3 SO-DIMM 內存條開始。 1.先看內存條的版本 從JEDEC下載…

Mysql練習題目【7月10日更新】

七、Mysql練習題目 https://zhuanlan.zhihu.com/p/38354000 1. 創建表 創建學生表 mysql> create table if not exists student(-> student_id varchar(255) not null,-> student_name varchar(255) not null,-> birthday date not null,-> gender varchar(…

前端面試題33(實時消息傳輸)

前端實時傳輸協議主要用于實現實時數據交換&#xff0c;特別是在Web應用中&#xff0c;它們讓開發者能夠構建具有實時功能的應用&#xff0c;如聊天、在線協作、游戲等。以下是幾種常見的前端實時傳輸協議的講解&#xff1a; 1. Short Polling (短輪詢) 原理&#xff1a;客戶…

【1】A-Frame整體介紹

1.A-Frame是什么&#xff1f; A-Frame 是一個用于構建虛擬現實 (VR) 體驗的 Web 框架。 A-Frame 基于 HTML 之上&#xff0c;因此上手簡單。但 A-Frame 不僅僅是 3D 場景圖或標記語言&#xff1b;它還是一種標記語言。其核心是一個強大的實體組件框架&#xff0c;為 Three.js …

Golang | Leetcode Golang題解之第226題翻轉二叉樹

題目&#xff1a; 題解&#xff1a; func invertTree(root *TreeNode) *TreeNode {if root nil {return nil}left : invertTree(root.Left)right : invertTree(root.Right)root.Left rightroot.Right leftreturn root }

AI機器人在未來的應用場景預測:是否會取代人類?華為、百度、特斯拉他們在AI領域都在做什么?

引言 隨著人工智能&#xff08;AI&#xff09;技術的飛速發展&#xff0c;AI機器人在各個領域的應用變得越來越普遍。從工業自動化到日常生活&#xff0c;AI機器人已經開始展現出強大的潛力和實際應用價值。本文將深入探討AI機器人在未來的應用場景&#xff0c;并分析它們是否…

uniapp+vue3嵌入Markdown格式

使用的庫是towxml 第一步&#xff1a;下載源文件&#xff0c;那么可以git clone&#xff0c;也可以直接下載壓縮包 git clone https://github.com/sbfkcel/towxml.git 第二步&#xff1a;設置文件夾內的config.js&#xff0c;可以選擇自己需要的格式 第三步&#xff1a;安裝…

大模型/NLP/算法面試題總結3——BERT和T5的區別?

1、BERT和T5的區別&#xff1f; BERT和T5是兩種著名的自然語言處理&#xff08;NLP&#xff09;模型&#xff0c;它們在架構、訓練方法和應用場景上有一些顯著的區別。以下是對這兩種模型的詳細比較&#xff1a; 架構 BERT&#xff08;Bidirectional Encoder Representation…