網站首頁 編程語言 正文
一、訪問者模式(Visitor Pattern)
數據結構中保存著許多元素,當我們希望改變一種對元素的處理方式時,要避免重復的修改數據結構。那么就要求我們在實現代碼時,將數據的處理進行分離,即:數據類只提供一個數據處理的接口,而該數據處理接口就被稱之為訪問者。那么,相同結構的數據面臨不同的處理結果時,我們只需要創建不同的訪問者。
訪問者模式,指作用于一個對象結構體上的元素的操作。訪問者可以使用戶在不改變該結構體中的類的基礎上定義一個新的操作。
優點:
- 使得在訪問者類中針對復雜類結構中的某個類添加新方法較為容易,即:只需要簡單地添加一個新的訪問者方法即可。如果不采用訪問者模式,這需要在每個類中添加一個新的方法。
- 訪問者將相關的方法集中在一個具體的訪問者類中,而其他相關的方法集中在另外一個具體的訪問者類中。也就是說,訪問者子類是按照方法的類型來分類的。
缺點:
- 增加一個具體的新
ConcreteElement
類比較困難。因為此時需要在每一個ConcreteVisitor
類中添加該ConcreteElement
類的訪問方法。
二、應用場景
當一個對象的結構中,包含有多種類型的具有不同接口的對象,且用戶要在這些對象上進行依賴于具體的類的運算時,需要用到訪問者模式。這就是為什么訪問者模式要針對每個被訪問的子類都設計一個不同的接口的原因。事實上,如果每個被訪問的子類都有相同的接口,包括構造方法、其他方法、參數都一致,則訪問者類只需要設計一個訪問方法,在該方法中含有一個用于區別不同的被訪問的子類的參數即可。例如:可以使用被訪問者基類作為參數類型。在對象的結構中包含有多種類型的有不同接口的對象時,各個不同的訪問方法可能為訪問所對應的類提供不同的參數類型。
當有多個不同的并且互不相關的運算將作用到這些對象上,而用戶不希望這些運算混淆這些類時,可以使用訪問者模式將相關的操作放到獨立的類中,例如:為了實現每個結點類中的計算價格方法,可以將所有的計算價格方法放到一個 VisitPrice
類中。
在對象的數據類型很少改變,但是需要經常改變操作或者增加新的操作的情況下,可以使用訪問者模式。反之,如果 Element
的子類經常改變結構,例如:需要增加一個新的稅種,這就需要在訪問者類中增加新的訪問方法,因此,在這種情況下使用訪問者模式代價較高,盡量不要使用訪問者模式。
三、代碼示例
該類圖包含兩個系列的類:“Element
類” 和 “訪問者類”,訪問者類定義了施加于 Element
類上的操作,為 Element 類提供一些功能。可以有多種具體的訪問者類,各自完成特定的目的,如一個訪問者類是計算價格,另一個訪問者類則是計算存貨數量。因此需要定義一個抽象的訪問者父類 Visitor 以及用于各種特殊目的具體的子類。Visitor 類必須給每個結點類提供一個操作,即訪問方法,例如獲得各結點所代表的商品對象的價格等。
實體角色組成:
-
Visitor:為每個 Element 的對象聲明一個訪問操作。該訪問操作的名字最好要包含被訪問的類的名字,以便確認該訪問操作是專門針對哪個具體的類,如:
visitFamilyNoChildren
是專門為了服務類 FamilyNoChildren 的。 -
ConcreteVisitor:實現 Visitor 聲明的運算。每個運算實現為對應的類的對象定義的算法的一部分。
ConcreteVisitor
提供算法的環境并且存儲其局部狀態。 - Element:定義了一些基本的方法,其中包含提供基本數據的方法,例如一些 get()與 set()方法。重要的是,每個 Element 子類都必須定義一個接收者方法,該方法以 Visitor 為參數類型:Accept(Visitor),其作用是為被訪問者對象和訪問者對象之間的交互提供接口。
-
ConcreteElement:具體的
Element
的子類,例如 ElementA,該類包含一個 accept 方法接收訪問者對象。另外,該類還可能定義一些其他的方法以幫助訪問者實現一些功能。 - ObjectStructure:提供一個高層接口,允許訪問者訪問 Element 的子類。在該類中可以包含一個結構,例如 ArrayList、Vector 等,提供所要訪問的 element 的列表。
示例:上市公司的原始財務數據:
- 對于會計來說需要制作各種報表
- 對于財務總監來說需要分析公司業績
- 對于戰略顧問來說需要分析行業變化
class Finance: ? ? """財務數據結構類""" ? ?? ? ? def __init__(self): ? ? ? ? self.salesvolume = None ? ? ? ? ?# 銷售額 ? ? ? ? self.cost = None ? ? ? ? ? ? ? ? # 成本 ? ? ? ? self.history_salesvolume = None ?# 歷史銷售額 ? ? ? ? self.history_cost = None ? ? ? ? # 歷史成本 ? ? def set_salesvolume(self, value): ? ? ? ? self.salesvolume = value ? ? def set_cost(self, value): ? ? ? ? self.cost = value ? ? def set_history_salesvolume(self, value): ? ? ? ? self.history_salesvolume = value ? ? def set_history_cost(self, value): ? ? ? ? self.history_cost = value ? ? def accept(self, visitor): ? ? ? ? pass class Finance_year(Finance): ? ? """2018 年財務數據類""" ? ? def __init__(self, year): ? ? ? ? Finance.__init__(self) ? ? ? ? self.work = [] ? ?# 安排工作人員列表 ? ? ? ? self.year = year ? ? def add_work(self, work): ? ? ? ? self.work.append(work) ? ? def accept(self): ? ? ? ? for obj in self.work: ? ? ? ? ? ? obj.visit(self) class Accounting: ? ? """會計類""" ? ? def __init__(self): ? ? ? ? self.ID = "會計" ? ? ? ? self.Duty = "計算報表" ? ? def visit(self, table): ? ? ? ? print('會計年度: {}'.format(table.year)) ? ? ? ? print("我的身份是: {} 職責: {}".format(self.ID, self.Duty)) ? ? ? ? print('本年度純利潤: {}'.format(table.salesvolume - table.cost)) ? ? ? ? print('------------------') class Audit: ? ? """財務總監類""" ? ? def __init__(self): ? ? ? ? self.ID = "財務總監" ? ? ? ? self.Duty = "分析業績" ? ? def visit(self, table): ? ? ? ? print('會計總監年度: {}'.format(table.year)) ? ? ? ? print("我的身份是: {} 職責: {}".format(self.ID, self.Duty)) ? ? ? ? if table.salesvolume - table.cost > table.history_salesvolume - table.history_cost: ? ? ? ? ? ? msg = "較同期上漲" ? ? ? ? else: ? ? ? ? ? ? msg = "較同期下跌" ? ? ? ? print('本年度公司業績: {}'.format(msg)) ? ? ? ? print('------------------') class Adviser: ? ? """戰略顧問""" ? ? def __init__(self): ? ? ? ? self.ID = "戰略顧問" ? ? ? ? self.Duty = "制定明年戰略" ? ? def visit(self, table): ? ? ? ? print('戰略顧問年度: {}'.format(table.year)) ? ? ? ? print("我的身份是: {} 職責: {}".format(self.ID, self.Duty)) ? ? ? ? if table.salesvolume > table.history_salesvolume: ? ? ? ? ? ? msg = "行業上行,擴大生產規模" ? ? ? ? else: ? ? ? ? ? ? msg = "行業下行,減小生產規模" ? ? ? ? print('本年度公司業績: {}'.format(msg)) ? ? ? ? print('------------------') class Work: ? ? """工作類""" ? ? def __init__(self): ? ? ? ? self.works = [] ?# 需要處理的年度數據列表 ? ? def add_work(self, obj): ? ? ? ? self.works.append(obj) ? ? def remove_work(self, obj): ? ? ? ? self.works.remove(obj) ? ? def visit(self): ? ? ? ? for obj in self.works: ? ? ? ? ? ? obj.accept() if __name__ == '__main__': ? ? work = Work() ?# 計劃安排財務、總監、顧問對2018年數據處理 ? ? # 實例化2018年數據結構 ? ? finance_2018 = Finance_year(2018) ? ? finance_2018.set_salesvolume(200) ? ? finance_2018.set_cost(100) ? ? finance_2018.set_history_salesvolume(180) ? ? finance_2018.set_history_cost(90) ? ? accounting = Accounting() ? # 實例化會計 ? ? audit = Audit() ?# 實例化總監 ? ? adviser = Adviser() ? ? # 實例化顧問 ? ? finance_2018.add_work(accounting) ? # 會計安排到2018分析日程中 ? ? finance_2018.add_work(audit) ? ?# 總監安排到2018分析日程中 ? ? finance_2018.add_work(adviser) ?# 顧問安排到2018分析日程中 ? ? work.add_work(finance_2018) # 添加2018年財務工作安排 ? ? work.visit()
原文鏈接:https://is-cloud.blog.csdn.net/article/details/122934106
相關推薦
- 2023-02-09 Python去除html標簽的幾種方法總結_python
- 2022-12-22 淺析Go語言中數組的這些細節_Golang
- 2023-10-09 markdown和富文本編輯器的區別
- 2022-06-22 Git中tag標簽的使用教程_其它綜合
- 2022-04-20 Selenium?三種等待方式(強制等待、隱式等待、顯示等待)_python
- 2022-07-02 C#并行編程之Task同步機制_C#教程
- 2022-07-17 Docker?Push?Skipped?foreign?layer?的錯誤問題及解決方案_docke
- 2023-01-14 python與matlab一些常用函數互轉問題_python
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支