實戰設計模式之訪問者模式

概述

????????訪問者模式允許我們在不改變類的前提下,向已有類添加新的功能。簡單來說,就是將算法與對象的數據結構進行分離的一種方法。在實際應用中,當我們需要對一組對象執行一些操作,而這些操作又需要隨著需求的變化而不斷變化時,訪問者模式就顯得尤為重要了。

????????電子商務平臺的庫存管理系統是現實生活中運用訪問者模式的一個典型例子。電子商務平臺會銷售不同種類的商品,比如:書籍、電子產品和服裝等。我們需要定期對庫存進行不同的統計分析,包括:計算總價值、統計數量等。隨著業務的發展,可能會有新的商品類型加入,也可能會有新的統計需求出現。訪問者模式允許我們將特定的商品處理邏輯與商品類本身分離,使得添加新的商品類型或處理邏輯變得更為簡單和靈活。

基本原理

????????訪問者模式的核心思想是:將算法與對象結構分離。具體來說,訪問者模式通過定義一個操作(通常稱為“訪問”),這個操作可以在不修改該元素的類的前提下,為每一個具體元素類聲明一個該操作。在訪問者模式中,我們有兩個主要的角色:一個是接受訪問的對象集合(即元素),另一個是對這些對象執行操作的訪問者。元素知道如何接受訪問者,并且會調用訪問者的相應方法來完成操作。

????????訪問者模式主要由以下五個核心組件構成。

????????1、訪問者。為每一個具體元素聲明一個訪問操作,表示訪問者訪問一個元素所要完成的工作。通常情況下,訪問者會包含多個Visit方法,每個方法對應一種具體的元素類型。

????????2、具體訪問者。實現了訪問者接口中的每種Visit方法,以完成具體的業務邏輯。

????????3、元素。定義了一個接受訪問者的接口Accept,其主要作用是讓訪問者訪問自身。同時,Element也是一個抽象類,代表了一組可以被訪問的對象。

????????4、具體元素。通常是那些需要被訪問的對象,實現了Element接口,并提供自身的具體實現。

????????5、對象結構。管理元素對象的集合,提供了遍歷元素的方法。

????????基于上面的核心組件,訪問者模式的實現主要有以下五個步驟。

????????1、定義元素接口。創建一個抽象類或接口,至少聲明了一個Accept方法。該方法用于接收一個訪問者對象,并調用訪問者的相應Visit方法來執行對當前元素的操作。

????????2、創建具體元素類。對于每一個需要被訪問的具體元素,創建一個繼承自元素接口的類。在每個具體元素類中,實現Accept方法,使其調用傳入的訪問者對象的對應Visit方法,并傳遞自身作為參數。

????????3、定義訪問者接口。創建一個接口,為每種具體訪問者類型定義一個Visit方法。這意味著,每當添加一個新的具體訪問者類型時,都需要向訪問者接口中添加一個新的Visit方法聲明。

????????4、實現具體訪問者類。根據具體的業務邏輯需求,創建實現訪問者接口的具體訪問者類。在這些類中,實現所有聲明的Visit方法,每個方法都包含了針對特定訪問者類型的處理邏輯。

????????5、創建對象結構。創建一個管理元素集合的對象結構,可以是一個簡單的容器,也可以是更復雜的數據結構。此對象結構應提供遍歷其內部元素的方法,并能夠接受一個訪問者對象,依次對其內部的每個元素調用Accept方法。

實戰代碼

????????在下面的實戰代碼中,我們使用訪問者模式模擬了電子商務平臺庫存管理系統的實現。

????????首先,我們定義了一個抽象基類CProduct,它包含商品的基本信息(名稱、價格、庫存量),并聲明了一個純虛函數Accept用于接收訪問者對象。

????????接著,我們定義了兩個具體的商品類CBook和CElectronics。它們繼承自CProduct,并實現了Accept方法。該方法調用傳入的訪問者的相應Visit方法,以執行針對具體商品類型的特定操作。

????????然后,我們定義了一個抽象訪問者類CVisitor。其中聲明了針對每種商品類型的Visit方法,并通過具體訪問者類CStockValueCalculator實現了這些方法,用來計算庫存總價值。此外,還有一個管理商品集合的對象結構類CInventory。它負責存儲商品實例,并提供了一個PerformCalculations方法遍歷所有商品,對每個商品調用其Accept方法,傳入具體的訪問者對象以執行相應的業務邏輯。

????????最后,在main函數中,我們創建了一個CInventory實例。在添加了幾種不同類型的商品后,我們使用CStockValueCalculator訪問者來計算庫存總價值,并最終輸出了結果。

#include <iostream>
#include <vector>
#include <string>using namespace std;class CVisitor;// 元素接口
class CProduct
{
public:CProduct(const string& name, double price, int stock) : m_strName(name), m_dbPrice(price), m_nStock(stock) {}virtual ~CProduct() {}virtual void Accept(CVisitor& visitor) = 0;string GetName() const { return m_strName; }double GetPrice() const { return m_dbPrice; }int GetStock() const { return m_nStock; }protected:string m_strName;double m_dbPrice;int m_nStock;
};// 具體元素:書籍
class CBook : public CProduct
{
public:CBook(const string& name, double price, int stock) : CProduct(name, price, stock) {}void Accept(CVisitor& visitor) override;
};// 具體元素:電子產品
class CElectronics : public CProduct
{
public:CElectronics(const string& name, double price, int stock) : CProduct(name, price, stock) {}void Accept(CVisitor& visitor) override;
};// 訪問者接口
class CVisitor
{
public:virtual ~CVisitor() {}virtual void Visit(CBook& book) = 0;virtual void Visit(CElectronics& electronics) = 0;
};// 具體訪問者:計算庫存總價值
class CStockValueCalculator : public CVisitor
{
public:void Visit(CBook& book) override{m_dbTotalValue += book.GetPrice() * book.GetStock();}void Visit(CElectronics& electronics) override{m_dbTotalValue += electronics.GetPrice() * electronics.GetStock();}double GetTotalValue() const { return m_dbTotalValue; }private:double m_dbTotalValue = 0.0;
};void CBook::Accept(CVisitor& visitor)
{visitor.Visit(*this);
}void CElectronics::Accept(CVisitor& visitor)
{visitor.Visit(*this);
}// 對象結構,用于管理商品集合
class CInventory
{
public:~CInventory(){for (CProduct* product : m_vctProduct){delete product;}}void AddProduct(CProduct* product){m_vctProduct.push_back(product);}void PerformCalculations(CVisitor& visitor){for (CProduct* product : m_vctProduct){product->Accept(visitor);}}private:vector<CProduct*> m_vctProduct;
};int main()
{CInventory inventory;inventory.AddProduct(new CBook("Effective C++", 50.0, 10));inventory.AddProduct(new CElectronics("Phone", 1999.99, 20));CStockValueCalculator calculator;inventory.PerformCalculations(calculator);cout << "Total stock value: " << calculator.GetTotalValue() << endl;return 0;
}

總結

????????通過將算法從對象結構中分離出來,訪問者模式使得每個角色(元素和訪問者)都只負責自己的部分。另外,訪問者模式使得添加新的操作變得容易,而不需要修改現有的類。只需創建一個新的訪問者類來實現所需的操作即可,無需改動已有的元素類。

????????但引入訪問者模式會增加系統的設計復雜度,特別是當對象結構中有大量不同類型元素時,需要為每種類型定義相應的Visit方法,增加了代碼量和理解難度。訪問者需要知道所有被訪問元素的具體類型和內部表示,這可能會導致訪問者與元素之間產生緊密耦合,從而破壞了封裝性。

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

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

相關文章

centos7.9使用docker-compose安裝kafka

docker-compose配置文件 services:zookeeper:image: confluentinc/cp-zookeeper:7.0.1hostname: zookeepercontainer_name: zookeeperports:- "2181:2181"environment:ZOOKEEPER_CLIENT_PORT: 2181ZOOKEEPER_TICK_TIME: 2000kafka:image: confluentinc/cp-kafka:7.0…

STM32:Modbus通信協議核心解析:關鍵通信技術

知識點1【 Modbus通信】 1、Modbus的概述 Modbus是OSI模型第七層的應用層報文傳輸協議 協議&#xff1a;說明有組包和解包的過程 2、通信機制 Modelbus是一個請求/應答協議 通信機制&#xff1a;主機輪詢&#xff0c;從機應答的機制。每個從設備有唯一的地址&#xff0c;主…

LeetCode 3362.零數組變換 III:貪心+優先隊列+差分數組——清晰題解

【LetMeFly】3362.零數組變換 III&#xff1a;貪心優先隊列差分數組——清晰題解 力扣題目鏈接&#xff1a;https://leetcode.cn/problems/zero-array-transformation-iii/ 給你一個長度為 n 的整數數組 nums 和一個二維數組 queries &#xff0c;其中 queries[i] [li, ri] …

ORM++ 封裝實戰指南:安全高效的 C++ MySQL 數據庫操作

ORM 封裝實戰指南&#xff1a;安全高效的 C MySQL 數據庫操作 一、環境準備 1.1 依賴安裝 # Ubuntu/Debian sudo apt-get install libmysqlclient-dev # CentOS sudo yum install mysql-devel# 編譯時鏈接庫 (-I 指定頭文件路徑 -L 指定庫路徑) g main.cpp -stdc17 -I/usr/i…

JESD204B 協議介紹

一、協議概述 JESD204B是由JEDEC&#xff08;固態技術協會&#xff09;制定的高速串行接口標準&#xff0c;專為模數轉換器&#xff08;ADC&#xff09;、數模轉換器&#xff08;DAC&#xff09;與邏輯器件&#xff08;如FPGA、ASIC&#xff09;之間的數據傳輸設計。其核心目標…

yolov8,c++案例匯總

文章目錄 引言多目標追蹤案例人體姿態估計算法手勢姿態估計算法目標分割算法 引言 以下案例,基于c,ncnn,yolov8既可以在windows10/11上部署, 也可以在安卓端部署, 也可以在嵌入式端部署, 服務器端可支持部署封裝為DLL,支持c/c#/java端調用 多目標追蹤案例 基于yolov8, ncnn,…

運動規劃實戰案例 | 圖解基于狀態晶格(State Lattice)的路徑規劃(附ROS C++/Python仿真)

目錄 1 控制采樣 vs 狀態采樣2 State Lattice路徑規劃2.1 算法流程2.2 Lattice運動基元生成2.3 幾何代價函數2.4 運動學約束啟發式 3 算法仿真3.1 ROS C仿真3.2 Python仿真 1 控制采樣 vs 狀態采樣 控制采樣的技術路線源自經典的運動學建模思想。這種方法將機器人的控制指令空…

BERT框架:自然語言處理的革命性突破

引言 在自然語言處理&#xff08;NLP&#xff09;領域&#xff0c;2018年Google推出的BERT&#xff08;Bidirectional Encoder Representations from Transformers&#xff09;框架無疑是一場革命。作為基于Transformer架構的雙向編碼器表示模型&#xff0c;BERT通過預訓練學習…

【Fifty Project - D31】

結束了一個超級消耗周末&#xff0c;滿安排之健身梅溪湖游泳做飯喝酒羽毛球賽 完全力竭了&#xff0c;久久不能恢復過來&#xff0c;暫停健身安排了 端午后再繼續 今日完成記錄 TimePlan完成情況7&#xff1a;30 - 8&#xff1a;10有氧爬坡√9&#xff1a;00 - 11&#xff1a;…

信息學奧賽一本通 1547:【 例 1】區間和

【題目鏈接】 ybt 1547&#xff1a;【 例 1】區間和 【題目考點】 1. 線段樹 2. 樹狀數組 【解題思路】 本題要求維護區間和&#xff0c;實現單點修改、區間查詢。 解法1&#xff1a;線段樹 線段樹原理&#xff0c;及實現方法見&#xff1a;洛谷 P3374 【模板】樹狀數組…

力扣面試150題--求根節點到葉節點數字之和

Day 48 題目描述 思路 我們利用sum這個全局變量來保存總和值&#xff0c;遞歸函數sum來計算每個根到葉子節點路徑所代表的數&#xff0c;由于我們需要遍歷到每條根到葉子節點的路徑&#xff0c;所有我采取了前序遍歷&#xff0c;如果不是葉子節點&#xff0c;就計算到該節點代…

DJI上云API官方demo學習

1、websocket&#xff0c;所在位置如下圖&#xff0c;調用的可以用//websocket搜索 2、用到的http客戶端&#xff0c;axios 3、很多和后端交互都是走的http請求

uniapp開發小程序,如何根據權限動態配置按鈕或頁面內容

前言 寫了好幾個項目&#xff0c;發現小程序對權限控制非常麻煩&#xff0c;于是有了這個想法&#xff0c;但是網上找了一圈沒有一個比較完善的講解&#xff0c;因為小程序不支持自定義指令&#xff0c;所以不能像后臺那樣方便&#xff0c;于是就將幾個博主的想法結合。 思路就…

LSTM+Transformer混合模型架構文檔

LSTMTransformer混合模型架構文檔 模型概述 本項目實現了一個LSTMTransformer混合模型&#xff0c;用于超臨界機組協調控制系統的數據驅動建模。該模型結合了LSTM的時序建模能力和Transformer的自注意力機制&#xff0c;能夠有效捕捉時間序列數據中的長期依賴關系和變量間的復…

測量尺子:多功能測量工具,科技改變生活

測量尺子是一款專業的測距儀測量萬能工具箱類型手機APP&#xff0c;旨在為用戶提供最貼心的測量助手。它擁有和現實測量儀器一樣的測量標準&#xff0c;更簡單便捷且精準的測量方式&#xff0c;最新AR科技測量更是大大拓寬了可以被測量的高度和深度。無論是日常使用、學習還是工…

結課作業01. 用戶空間 MPU6050 體感鼠標驅動程序

目錄 一. qt界面實現 二. 虛擬設備模擬模擬鼠標實現體感鼠標 2.1 函數聲明 2.2 虛擬鼠標實現 2.2.1 虛擬鼠標創建函數 2.2.2 鼠標移動函數 2.2.3 鼠標點擊函數 2.3 mpu6050相關函數實現 2.3.1 i2c設備初始化 2.3.2 mpu6050寄存器寫入 2.3.3 mpu6050寄存器讀取 2.3.…

深入淺出 Python Testcontainers:用容器優雅地編寫集成測試

在現代軟件開發中&#xff0c;自動化測試已成為敏捷開發與持續集成中的關鍵環節。單元測試可以快速驗證函數或類的行為是否符合預期&#xff0c;而集成測試則確保多個模塊協同工作時依然正確。問題是&#xff1a;如何讓集成測試可靠、可重復且易于維護&#xff1f; 這時&#…

JVM 的垃圾回收器

新生代回收器 通性 會觸發StW&#xff0c;暫停所有應用線程復制算法 Serial 單線程回收適合單線程系統 ParNew 多線程回收優先保證響應速度&#xff0c;降低 STW&#xff08;STW 越大&#xff0c;執行垃圾回收的時間越長&#xff0c;回收的垃圾越多&#xff0c;減少垃圾回…

【筆記】排查并解決Error in LLM call after 3 attempts: (status code: 502)

#工作記錄 一、問題描述 在部署運行部署對沖基金分析工具 ai-hedge-fund 時&#xff0c;不斷出現以下報錯&#xff0c;導致項目運行異常&#xff1a; Error in LLM call after 3 attempts: (status code: 502) Error in LLM call after 3 attempts: [WinError 10054] 遠程主…

GO 語言進階之 Template 模板使用

更多個人筆記見&#xff1a; github個人筆記倉庫 gitee 個人筆記倉庫 個人學習&#xff0c;學習過程中還會不斷補充&#xff5e; &#xff08;后續會更新在github上&#xff09; 文章目錄 Template 模板基本示例語法1. 基本輸出語法2. 控制結構3. 空白字符控制4. Must函數 Temp…