【設計模式】03-理解常見設計模式-行為型模式(專欄完結)

前言

前面我們介紹完創建型模式和創建型模式,這篇介紹最后的行為型模式,也是【設計模式】專欄的最后一篇。


一、概述

行為型模式主要用于處理對象之間的交互和職責分配,以實現更靈活的行為和更好的協作。

二、常見的行為型模式

1、觀察者模式(Observer Pattern)

定義對象間的一種一對多的依賴關系,當一個對象的狀態發生改變時,所有依賴它的對象都會得到通知并自動更新。

解釋:可以把它想象成一個明星和粉絲的關系,明星(被觀察對象)的一舉一動(狀態改變)都會被粉絲(觀察者)關注到,當明星有新動態時,粉絲會收到消息。

代碼示范以及解釋?

// 被觀察對象類
class Subject {constructor() {this.observers = [];}// 添加觀察者addObserver(observer) {this.observers.push(observer);}// 移除觀察者removeObserver(observer) {const index = this.observers.indexOf(observer);if (index!== -1) {this.observers.splice(index, 1);}}// 通知所有觀察者notify() {this.observers.forEach(observer => observer.update());}
}// 觀察者類
class Observer {constructor(name) {this.name = name;}update() {console.log(`${this.name} has been notified.`);}
}// 使用示例
const subject = new Subject();
const observer1 = new Observer('Fan1');
const observer2 = new Observer('Fan2');subject.addObserver(observer1);
subject.addObserver(observer2);subject.notify(); 
// 輸出:
// Fan1 has been notified.
// Fan2 has been notified.

?被觀察的對象Subject

  • 構造函數?constructor
    • 初始化一個空數組?this.observers,用于存儲所有注冊的觀察者。
  • addObserver?方法
    • 接收一個?observer?對象作為參數,將其添加到?this.observers?數組中。這就相當于有一個新的觀察者開始關注這個被觀察對象了。
  • removeObserver?方法
    • 接收一個?observer?對象作為參數,首先使用?indexOf?方法查找該觀察者在?this.observers?數組中的索引。
    • 如果索引不為 -1(說明該觀察者存在于數組中),則使用?splice?方法將其從數組中移除,意味著這個觀察者不再關注被觀察對象了。
  • notify?方法
    • 遍歷?this.observers?數組,對每個觀察者調用其?update?方法。這樣就可以通知所有注冊的觀察者,讓它們執行相應的更新操作。

?觀察者Observer

  • 構造函數?constructor
    • 接收一個?name?參數,用于標識這個觀察者,將其存儲在?this.name?中。
  • update?方法
    • 當被觀察對象調用?notify?方法通知觀察者時,這個?update?方法會被執行。它會打印出一條消息,表明該觀察者已經收到了通知。

代碼執行結果解釋

  • 首先創建一個?Subject?類的實例?subject,表示被觀察對象。
  • 接著創建兩個?Observer?類的實例?observer1?和?observer2,分別命名為?'Fan1'?和?'Fan2'
  • 調用?subject.addObserver?方法將?observer1?和?observer2?添加到?subject?的觀察者列表中。
  • 最后調用?subject.notify()?方法,subject?會遍歷其觀察者列表,依次調用每個觀察者的?update?方法,因此控制臺會輸出?'Fan1 has been notified.'?和?'Fan2 has been notified.'

2、策略模式(Strategy Pattern):

定義一系列的算法,把它們一個個封裝起來,并且使它們可相互替換。策略模式讓算法的變化獨立于使用算法的客戶。

解釋:策略模式定義了一系列的算法,把它們一個個封裝起來,并且使它們可相互替換。策略模式讓算法的變化獨立于使用算法的客戶。這就好比你要去上班,有多種出行方式(策略)可供選擇,如坐公交、打車、騎自行車等,你可以根據當天的實際情況(如時間、天氣等)來動態地選擇合適的出行方式,而上班這個行為本身不受具體出行方式的影響。

?代碼示范以及解釋?

// 定義不同的策略類
// 公交出行策略
class BusStrategy {travel() {console.log('Taking the bus to work.');}
}
//這是一個公交出行策略類,包含一個 travel 方法。
//當調用 travel 方法時,會在控制臺打印出 Taking the bus to work.,表示選擇乘坐公交去上班。// 打車出行策略
class TaxiStrategy {travel() {console.log('Taking a taxi to work.');}
}
//這是一個打車出行策略類,同樣有一個 travel 方法。
//調用 travel 方法時,會在控制臺打印出 Taking a taxi to work.,表示選擇打車去上班。// 自行車出行策略
class BicycleStrategy {travel() {console.log('Riding a bicycle to work.');}
}
//這是一個自行車出行策略類,travel 方法會在控制臺打印出 Riding a bicycle to work.,表示選擇騎自行車去上班。// 環境類,負責使用策略
class Commute {constructor(strategy) {this.strategy = strategy;}setStrategy(strategy) {this.strategy = strategy;}goToWork() {this.strategy.travel();}
}
//構造函數 constructor:
//接收一個 strategy 參數,將傳入的策略對象賦值給 this.strategy,表示初始化時使用該策略。
//setStrategy 方法:
//接收一個新的 strategy 參數,將 this.strategy 更新為新的策略對象,從而實現策略的動態切換。
//goToWork 方法:
//調用當前 this.strategy 對象的 travel 方法,執行具體的出行策略。// 使用示例
const busStrategy = new BusStrategy();
const commute = new Commute(busStrategy);
commute.goToWork(); // 輸出: Taking the bus to work.const taxiStrategy = new TaxiStrategy();
commute.setStrategy(taxiStrategy);
commute.goToWork(); // 輸出: Taking a taxi to work.

執行結果解釋

  • 首先創建一個?BusStrategy?類的實例?busStrategy,表示公交出行策略。
  • 接著創建一個?Commute?類的實例?commute,并將?busStrategy?作為參數傳入,意味著初始化時使用公交出行策略。
  • 調用?commute.goToWork()?方法,會執行公交出行策略,控制臺輸出?Taking the bus to work.
  • 然后創建一個?TaxiStrategy?類的實例?taxiStrategy,表示打車出行策略。
  • 調用?commute.setStrategy(taxiStrategy)?方法,將當前的出行策略切換為打車策略。
  • 再次調用?commute.goToWork()?方法,會執行打車出行策略,控制臺輸出?Taking a taxi to work.

3、發布-訂閱模式(Publish - Subscribe Pattern)(重點!!)

發布 - 訂閱模式和觀察者模式類似,但它引入了一個中間者(消息代理),發布者(發布消息的對象)將消息發布到消息代理,訂閱者(接收消息的對象)向消息代理訂閱感興趣的消息。這種模式實現了發布者和訂閱者之間的解耦。

簡單理解:就像一個報社和讀者的關系,報社(發布者)負責發布報紙(消息),讀者(訂閱者)向報社訂閱報紙,當有新報紙出版時,報社將報紙發送給訂閱的讀者。

?代碼示范以及解釋?

// 消息代理類
class EventEmitter {constructor() {this.events = {};}// 訂閱事件on(eventName, callback) {if (!this.events[eventName]) {this.events[eventName] = [];}this.events[eventName].push(callback);}// 發布事件emit(eventName, ...args) {if (this.events[eventName]) {this.events[eventName].forEach(callback => callback(...args));}}// 取消訂閱事件off(eventName, callback) {if (this.events[eventName]) {const index = this.events[eventName].indexOf(callback);if (index!== -1) {this.events[eventName].splice(index, 1);}}}
}// 使用示例
const eventEmitter = new EventEmitter();// 訂閱者的回調函數
const callback1 = (message) => {console.log(`Subscriber 1 received: ${message}`);
};const callback2 = (message) => {console.log(`Subscriber 2 received: ${message}`);
};// 訂閱事件
eventEmitter.on('news', callback1);
eventEmitter.on('news', callback2);// 發布事件
eventEmitter.emit('news', 'A new article is published!'); 
// 輸出:
// Subscriber 1 received: A new article is published!
// Subscriber 2 received: A new article is published!// 取消訂閱
eventEmitter.off('news', callback1);
eventEmitter.emit('news', 'Another new article!'); 
// 輸出:
// Subscriber 2 received: Another new article!

整體功能概述

EventEmitter?類充當消息代理,它允許用戶訂閱(on?方法)特定的事件,發布(emit?方法)事件,以及取消訂閱(off?方法)事件。當事件被發布時,所有訂閱該事件的回調函數都會被執行。

消息代理類?EventEmitter

  • 構造函數?constructor
    • 初始化一個空對象?this.events,用于存儲事件及其對應的回調函數數組。每個事件名作為對象的鍵,對應的值是一個數組,數組中存儲著訂閱該事件的所有回調函數。
  • on?方法
    • 接收兩個參數:eventName?表示事件的名稱,callback?是訂閱該事件時要執行的回調函數。
    • 首先檢查?this.events?對象中是否已經存在該事件名。如果不存在,就為該事件名創建一個空數組。
    • 然后將傳入的?callback?函數添加到該事件對應的數組中。這意味著又有一個訂閱者訂閱了該事件。
  • emit?方法
    • 接收事件名?eventName?和任意數量的參數?...args
    • 檢查?this.events?對象中是否存在該事件名。如果存在,就遍歷該事件對應的回調函數數組,依次調用每個回調函數,并將?...args?作為參數傳遞給它們。這樣就實現了事件的發布,通知所有訂閱者執行相應的操作。
  • off?方法
    • 接收事件名?eventName?和要取消訂閱的回調函數?callback
    • 檢查?this.events?對象中是否存在該事件名。如果存在,使用?indexOf?方法查找該回調函數在事件對應的數組中的索引。
    • 如果索引不為 -1(說明該回調函數存在于數組中),使用?splice?方法將其從數組中移除,從而實現取消訂閱的功能。

執行結果解釋

const eventEmitter = new EventEmitter();// 訂閱者的回調函數
const callback1 = (message) => {console.log(`Subscriber 1 received: ${message}`);
};const callback2 = (message) => {console.log(`Subscriber 2 received: ${message}`);
};// 訂閱事件
eventEmitter.on('news', callback1);
eventEmitter.on('news', callback2);// 發布事件
eventEmitter.emit('news', 'A new article is published!'); 
// 輸出:
// Subscriber 1 received: A new article is published!
// Subscriber 2 received: A new article is published!// 取消訂閱
eventEmitter.off('news', callback1);
eventEmitter.emit('news', 'Another new article!'); 
// 輸出:
// Subscriber 2 received: Another new article!
  • 創建一個?EventEmitter?類的實例?eventEmitter
  • 定義兩個回調函數?callback1?和?callback2,分別表示兩個訂閱者在收到事件通知時要執行的操作。
  • 調用?eventEmitter.on?方法,將?callback1?和?callback2?訂閱到?'news'?事件上。
  • 調用?eventEmitter.emit?方法發布?'news'?事件,并傳遞消息?'A new article is published!'。由于?callback1?和?callback2?都訂閱了該事件,所以它們都會被執行,控制臺會輸出相應的消息。
  • 調用?eventEmitter.off?方法取消?callback1?對?'news'?事件的訂閱。
  • 再次調用?eventEmitter.emit?方法發布?'news'?事件,并傳遞消息?'Another new article!'。此時只有?callback2?還訂閱著該事件,所以只有?callback2?會被執行,控制臺會輸出相應的消息。

三、總結

?到此,【設計模式】專欄的篇章已經完結~

文章介紹的是部分常見的設計模式,實際上還有很多設計模式等著大家去學習,后續我有空會補充新的設計模式內容,敬請期待吧~

如果你喜歡這篇文章,留下你的三連+訂閱~

關注我,及時獲取最新文章消息~

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

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

相關文章

mapbox基礎,使用geojson加載line線圖層,實現純色填充、圖片填充、虛線和漸變效果

????? 主頁: gis分享者 ????? 感謝各位大佬 點贊?? 收藏? 留言?? 加關注?! ????? 收錄于專欄:mapbox 從入門到精通 文章目錄 一、??前言1.1 ??mapboxgl.Map 地圖對象1.2 ??mapboxgl.Map style屬性1.3 ??line線圖層樣式二、??使用geojson加載…

深入淺出:CUDA是什么,如何利用它進行高效并行計算

在當今這個數據驅動的時代,計算能力的需求日益增加,特別是在深度學習、科學計算和圖像處理等領域。為了滿足這些需求,NVIDIA推出了CUDA(Compute Unified Device Architecture),這是一種并行計算平臺和編程模…

LNMP+Zabbix安裝部署(Zabbix6.0 Lnmp+Zabbix Installation and Deployment)

LNMPZabbix安裝部署(Zabbix6.0) 簡介 LNMP(Linux Nginx MySQL PHP)是一種流行的Web服務器架構,廣泛用于搭建高性能的網站和應用程序。Zabbix 是一個開源的監控軟件,可以用來監控網絡、服務器和應用程序…

Docker 部署 Dify:輕松集成 Ollama 和 DeepSeek

1 Ollama的安裝及使用 1.1 什么是Ollama? Ollama 是一個用于本地部署和運行大型語言模型的框架。 Ollama 的作用包括: 本地模型運行:Ollama 允許在本地機器上運行大型語言模型(如 LLaMA、DeepSeek 等),無…

C++筆記之標準庫中用于處理迭代器的`std::advance`和`std::distance`

C++筆記之標準庫中用于處理迭代器的std::advance和std::distance code review! 文章目錄 C++筆記之標準庫中用于處理迭代器的`std::advance`和`std::distance`一.`std::advance`函數原型參數說明使用場景示例代碼示例 1:移動 `std::vector` 的隨機訪問迭代器示例 2:移動 `st…

工業制造能耗管理新突破,漫途MTIC-ECM平臺助力企業綠色轉型!

在工業制造領域,能源消耗一直是企業運營成本的重要組成部分。隨著“雙碳”目標的推進,如何實現高效能耗管理,成為制造企業亟待解決的問題。漫途MTIC-ECM能源能耗在線監測平臺,結合其自研的硬件產品,為工業制造企業提供…

C語言——深入理解指針(2)(數組與指針)

文章目錄 數組名的理解使用指針訪問數組一維數組傳參的本質冒泡排序二級指針指針數組指針數組模擬二維數組 數組名的理解 之前我們在使用指針訪問數組內容時,有這樣的代碼: int arr[10]{1,2,3,4,5,6,7,8,9,10}; int* p&arr[0];這里我們使用&ar…

在Windows系統中安裝Open WebUI并連接Ollama

Open WebUI是一個開源的大語言模型(LLM)交互界面,支持本地部署與離線運行。通過它,用戶可以在類似ChatGPT的網頁界面中,直接操作本地運行的Ollama等大語言模型工具。 安裝前的核心要求: Python 3.11&#…

Day4:強化學習之Qlearning走迷宮

一、迷宮游戲 1.環境已知 迷宮環境是定義好的,障礙物位置和空位置是已知的; # 定義迷宮 grid [[0, 0, 0, 1, 0],[0, 1, 0, 1, 0],[0, 1, 0, 0, 0],[0, 0, 0, 1, 0],[0, 1, 1, 1, 0] ] 2.獎勵方式已知 如果碰到障礙物則得-1,如果到終點則…

家里WiFi信號穿墻后信號太差怎么處理?

一、首先在調制解調器(俗稱:貓)測試網速,網速達不到聯系運營商; 二、網線影響不大,5類網線跑500M完全沒問題; 三、可以在臥室增加輔助路由器(例如小米AX系列)90~200元區…

視點開場動畫實現(九)

這個相對比較簡單: void COSGObject::FlyTo(double lon, double lat, double hei) {theApp.bNeedModify TRUE;while(!theApp.bCanModify)Sleep(1);em->setViewpoint(osgEarth::Viewpoint("0",lon, lat, 0, 0, -45, hei), 2);theApp.bNeedModify FAL…

保姆級GitHub大文件(100mb-2gb)上傳教程

GLF(Git Large File Storage)安裝使用 使用GitHub desktop上傳大于100mb的文件時報錯 The following files are over 100MB. lf you commit these files, you will no longer beable to push this repository to GitHub.com.term.rarWe recommend you a…

HTML之JavaScript DOM(document)編程處理事件

HTML之JavaScript DOM&#xff08;document&#xff09;編程處理事件 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"…

Redis7——基礎篇(四)

前言&#xff1a;此篇文章系本人學習過程中記錄下來的筆記&#xff0c;里面難免會有不少欠缺的地方&#xff0c;誠心期待大家多多給予指教。 基礎篇&#xff1a; Redis&#xff08;一&#xff09;Redis&#xff08;二&#xff09;Redis&#xff08;三&#xff09; 接上期內容&…

Sprinig源碼解析

前言 Spring 框架是 Java 企業級開發的基石&#xff0c;其源碼設計體現了模塊化、擴展性和靈活性。以下從 IoC 容器、AOP 實現、核心模塊和關鍵設計模式四個角度對 Spring 源碼進行深度解析&#xff0c;幫助理解其底層機制。即使Spring會使用的人見得就能使用。 一、IoC 容器源…

如何簡單的去使用jconsloe 查看線程 (多線程編程篇1)

目錄 前言 1.進程和線程 進程 PCB 的作用 并發編程和并行編程 線程 為什么選擇多線程編程 2.在IDEA中如何簡單創建一個線程 1. 通過繼承Thread類 2. 通過實現 Runnable 接口 3. 使用 Lambda 表達式 3.如何簡單使用jconsloe去查看創建好的線程 前言 2025來了,這是第…

【ISO 14229-1:2023 UDS診斷(ECU復位0x11服務)測試用例CAPL代碼全解析④】

ISO 14229-1:2023 UDS診斷【ECU復位0x11服務】_TestCase04 作者&#xff1a;車端域控測試工程師 更新日期&#xff1a;2025年02月17日 關鍵詞&#xff1a;UDS診斷協議、ECU復位服務、0x11服務、ISO 14229-1:2023 TC11-004測試用例 用例ID測試場景驗證要點參考條款預期結果TC…

3.10 實戰Hugging Face Transformers:從文本分類到模型部署全流程

實戰Hugging Face Transformers:從文本分類到模型部署全流程 一、文本分類實戰:IMDB電影評論情感分析 1.1 數據準備與預處理 from datasets import load_dataset from transformers import AutoTokenizer # 加載IMDB數據集 dataset = load_dataset("imdb") …

【人工智能】釋放數據潛能:使用Featuretools進行自動化特征工程

《Python OpenCV從菜鳥到高手》帶你進入圖像處理與計算機視覺的大門! 解鎖Python編程的無限可能:《奇妙的Python》帶你漫游代碼世界 特征工程是機器學習流程中至關重要的一步,它直接影響模型的性能。然而,手動特征工程既耗時又需要領域專業知識。Featuretools是一個強大的…

MybaitsPlus學習筆記(二)基本CURD

目錄 一、BaseMapper 二、常用實例 1、插入 2、刪除 3、修改 4、查詢 三、IService 四、 IService中的一些方法測試 一、BaseMapper MyBatis-Plus中的基本CRUD在內置的BaseMapper中都已得到了實現&#xff0c;我們可以直接使用&#xff0c;接口如 下&#xff1a; publ…