網(wǎng)站首頁(yè) 編程語(yǔ)言 正文
一、訪(fǎng)問(wèn)者模式(Visitor Pattern)
數(shù)據(jù)結(jié)構(gòu)中保存著許多元素,當(dāng)我們希望改變一種對(duì)元素的處理方式時(shí),要避免重復(fù)的修改數(shù)據(jù)結(jié)構(gòu)。那么就要求我們?cè)趯?shí)現(xiàn)代碼時(shí),將數(shù)據(jù)的處理進(jìn)行分離,即:數(shù)據(jù)類(lèi)只提供一個(gè)數(shù)據(jù)處理的接口,而該數(shù)據(jù)處理接口就被稱(chēng)之為訪(fǎng)問(wèn)者。那么,相同結(jié)構(gòu)的數(shù)據(jù)面臨不同的處理結(jié)果時(shí),我們只需要?jiǎng)?chuàng)建不同的訪(fǎng)問(wèn)者。
訪(fǎng)問(wèn)者模式,指作用于一個(gè)對(duì)象結(jié)構(gòu)體上的元素的操作。訪(fǎng)問(wèn)者可以使用戶(hù)在不改變?cè)摻Y(jié)構(gòu)體中的類(lèi)的基礎(chǔ)上定義一個(gè)新的操作。
優(yōu)點(diǎn):
- 使得在訪(fǎng)問(wèn)者類(lèi)中針對(duì)復(fù)雜類(lèi)結(jié)構(gòu)中的某個(gè)類(lèi)添加新方法較為容易,即:只需要簡(jiǎn)單地添加一個(gè)新的訪(fǎng)問(wèn)者方法即可。如果不采用訪(fǎng)問(wèn)者模式,這需要在每個(gè)類(lèi)中添加一個(gè)新的方法。
- 訪(fǎng)問(wèn)者將相關(guān)的方法集中在一個(gè)具體的訪(fǎng)問(wèn)者類(lèi)中,而其他相關(guān)的方法集中在另外一個(gè)具體的訪(fǎng)問(wèn)者類(lèi)中。也就是說(shuō),訪(fǎng)問(wèn)者子類(lèi)是按照方法的類(lèi)型來(lái)分類(lèi)的。
缺點(diǎn):
- 增加一個(gè)具體的新
ConcreteElement
類(lèi)比較困難。因?yàn)榇藭r(shí)需要在每一個(gè)ConcreteVisitor
類(lèi)中添加該ConcreteElement
類(lèi)的訪(fǎng)問(wèn)方法。
二、應(yīng)用場(chǎng)景
當(dāng)一個(gè)對(duì)象的結(jié)構(gòu)中,包含有多種類(lèi)型的具有不同接口的對(duì)象,且用戶(hù)要在這些對(duì)象上進(jìn)行依賴(lài)于具體的類(lèi)的運(yùn)算時(shí),需要用到訪(fǎng)問(wèn)者模式。這就是為什么訪(fǎng)問(wèn)者模式要針對(duì)每個(gè)被訪(fǎng)問(wèn)的子類(lèi)都設(shè)計(jì)一個(gè)不同的接口的原因。事實(shí)上,如果每個(gè)被訪(fǎng)問(wèn)的子類(lèi)都有相同的接口,包括構(gòu)造方法、其他方法、參數(shù)都一致,則訪(fǎng)問(wèn)者類(lèi)只需要設(shè)計(jì)一個(gè)訪(fǎng)問(wèn)方法,在該方法中含有一個(gè)用于區(qū)別不同的被訪(fǎng)問(wèn)的子類(lèi)的參數(shù)即可。例如:可以使用被訪(fǎng)問(wèn)者基類(lèi)作為參數(shù)類(lèi)型。在對(duì)象的結(jié)構(gòu)中包含有多種類(lèi)型的有不同接口的對(duì)象時(shí),各個(gè)不同的訪(fǎng)問(wèn)方法可能為訪(fǎng)問(wèn)所對(duì)應(yīng)的類(lèi)提供不同的參數(shù)類(lèi)型。
當(dāng)有多個(gè)不同的并且互不相關(guān)的運(yùn)算將作用到這些對(duì)象上,而用戶(hù)不希望這些運(yùn)算混淆這些類(lèi)時(shí),可以使用訪(fǎng)問(wèn)者模式將相關(guān)的操作放到獨(dú)立的類(lèi)中,例如:為了實(shí)現(xiàn)每個(gè)結(jié)點(diǎn)類(lèi)中的計(jì)算價(jià)格方法,可以將所有的計(jì)算價(jià)格方法放到一個(gè) VisitPrice
類(lèi)中。
在對(duì)象的數(shù)據(jù)類(lèi)型很少改變,但是需要經(jīng)常改變操作或者增加新的操作的情況下,可以使用訪(fǎng)問(wèn)者模式。反之,如果 Element
的子類(lèi)經(jīng)常改變結(jié)構(gòu),例如:需要增加一個(gè)新的稅種,這就需要在訪(fǎng)問(wèn)者類(lèi)中增加新的訪(fǎng)問(wèn)方法,因此,在這種情況下使用訪(fǎng)問(wèn)者模式代價(jià)較高,盡量不要使用訪(fǎng)問(wèn)者模式。
三、代碼示例
該類(lèi)圖包含兩個(gè)系列的類(lèi):“Element
類(lèi)” 和 “訪(fǎng)問(wèn)者類(lèi)”,訪(fǎng)問(wèn)者類(lèi)定義了施加于 Element
類(lèi)上的操作,為 Element 類(lèi)提供一些功能。可以有多種具體的訪(fǎng)問(wèn)者類(lèi),各自完成特定的目的,如一個(gè)訪(fǎng)問(wèn)者類(lèi)是計(jì)算價(jià)格,另一個(gè)訪(fǎng)問(wèn)者類(lèi)則是計(jì)算存貨數(shù)量。因此需要定義一個(gè)抽象的訪(fǎng)問(wèn)者父類(lèi) Visitor 以及用于各種特殊目的具體的子類(lèi)。Visitor 類(lèi)必須給每個(gè)結(jié)點(diǎn)類(lèi)提供一個(gè)操作,即訪(fǎng)問(wèn)方法,例如獲得各結(jié)點(diǎn)所代表的商品對(duì)象的價(jià)格等。
實(shí)體角色組成:
-
Visitor:為每個(gè) Element 的對(duì)象聲明一個(gè)訪(fǎng)問(wèn)操作。該訪(fǎng)問(wèn)操作的名字最好要包含被訪(fǎng)問(wèn)的類(lèi)的名字,以便確認(rèn)該訪(fǎng)問(wèn)操作是專(zhuān)門(mén)針對(duì)哪個(gè)具體的類(lèi),如:
visitFamilyNoChildren
是專(zhuān)門(mén)為了服務(wù)類(lèi) FamilyNoChildren 的。 -
ConcreteVisitor:實(shí)現(xiàn) Visitor 聲明的運(yùn)算。每個(gè)運(yùn)算實(shí)現(xiàn)為對(duì)應(yīng)的類(lèi)的對(duì)象定義的算法的一部分。
ConcreteVisitor
提供算法的環(huán)境并且存儲(chǔ)其局部狀態(tài)。 - Element:定義了一些基本的方法,其中包含提供基本數(shù)據(jù)的方法,例如一些 get()與 set()方法。重要的是,每個(gè) Element 子類(lèi)都必須定義一個(gè)接收者方法,該方法以 Visitor 為參數(shù)類(lèi)型:Accept(Visitor),其作用是為被訪(fǎng)問(wèn)者對(duì)象和訪(fǎng)問(wèn)者對(duì)象之間的交互提供接口。
-
ConcreteElement:具體的
Element
的子類(lèi),例如 ElementA,該類(lèi)包含一個(gè) accept 方法接收訪(fǎng)問(wèn)者對(duì)象。另外,該類(lèi)還可能定義一些其他的方法以幫助訪(fǎng)問(wèn)者實(shí)現(xiàn)一些功能。 - ObjectStructure:提供一個(gè)高層接口,允許訪(fǎng)問(wèn)者訪(fǎng)問(wèn) Element 的子類(lèi)。在該類(lèi)中可以包含一個(gè)結(jié)構(gòu),例如 ArrayList、Vector 等,提供所要訪(fǎng)問(wèn)的 element 的列表。
示例:上市公司的原始財(cái)務(wù)數(shù)據(jù):
- 對(duì)于會(huì)計(jì)來(lái)說(shuō)需要制作各種報(bào)表
- 對(duì)于財(cái)務(wù)總監(jiān)來(lái)說(shuō)需要分析公司業(yè)績(jī)
- 對(duì)于戰(zhàn)略顧問(wèn)來(lái)說(shuō)需要分析行業(yè)變化
class Finance: ? ? """財(cái)務(wù)數(shù)據(jù)結(jié)構(gòu)類(lèi)""" ? ?? ? ? def __init__(self): ? ? ? ? self.salesvolume = None ? ? ? ? ?# 銷(xiāo)售額 ? ? ? ? self.cost = None ? ? ? ? ? ? ? ? # 成本 ? ? ? ? self.history_salesvolume = None ?# 歷史銷(xiāo)售額 ? ? ? ? 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 年財(cái)務(wù)數(shù)據(jù)類(lèi)""" ? ? 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: ? ? """會(huì)計(jì)類(lèi)""" ? ? def __init__(self): ? ? ? ? self.ID = "會(huì)計(jì)" ? ? ? ? self.Duty = "計(jì)算報(bào)表" ? ? def visit(self, table): ? ? ? ? print('會(huì)計(jì)年度: {}'.format(table.year)) ? ? ? ? print("我的身份是: {} 職責(zé): {}".format(self.ID, self.Duty)) ? ? ? ? print('本年度純利潤(rùn): {}'.format(table.salesvolume - table.cost)) ? ? ? ? print('------------------') class Audit: ? ? """財(cái)務(wù)總監(jiān)類(lèi)""" ? ? def __init__(self): ? ? ? ? self.ID = "財(cái)務(wù)總監(jiān)" ? ? ? ? self.Duty = "分析業(yè)績(jī)" ? ? def visit(self, table): ? ? ? ? print('會(huì)計(jì)總監(jiān)年度: {}'.format(table.year)) ? ? ? ? print("我的身份是: {} 職責(zé): {}".format(self.ID, self.Duty)) ? ? ? ? if table.salesvolume - table.cost > table.history_salesvolume - table.history_cost: ? ? ? ? ? ? msg = "較同期上漲" ? ? ? ? else: ? ? ? ? ? ? msg = "較同期下跌" ? ? ? ? print('本年度公司業(yè)績(jī): {}'.format(msg)) ? ? ? ? print('------------------') class Adviser: ? ? """戰(zhàn)略顧問(wèn)""" ? ? def __init__(self): ? ? ? ? self.ID = "戰(zhàn)略顧問(wèn)" ? ? ? ? self.Duty = "制定明年戰(zhàn)略" ? ? def visit(self, table): ? ? ? ? print('戰(zhàn)略顧問(wèn)年度: {}'.format(table.year)) ? ? ? ? print("我的身份是: {} 職責(zé): {}".format(self.ID, self.Duty)) ? ? ? ? if table.salesvolume > table.history_salesvolume: ? ? ? ? ? ? msg = "行業(yè)上行,擴(kuò)大生產(chǎn)規(guī)模" ? ? ? ? else: ? ? ? ? ? ? msg = "行業(yè)下行,減小生產(chǎn)規(guī)模" ? ? ? ? print('本年度公司業(yè)績(jī): {}'.format(msg)) ? ? ? ? print('------------------') class Work: ? ? """工作類(lèi)""" ? ? def __init__(self): ? ? ? ? self.works = [] ?# 需要處理的年度數(shù)據(jù)列表 ? ? 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() ?# 計(jì)劃安排財(cái)務(wù)、總監(jiān)、顧問(wèn)對(duì)2018年數(shù)據(jù)處理 ? ? # 實(shí)例化2018年數(shù)據(jù)結(jié)構(gòu) ? ? 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() ? # 實(shí)例化會(huì)計(jì) ? ? audit = Audit() ?# 實(shí)例化總監(jiān) ? ? adviser = Adviser() ? ? # 實(shí)例化顧問(wèn) ? ? finance_2018.add_work(accounting) ? # 會(huì)計(jì)安排到2018分析日程中 ? ? finance_2018.add_work(audit) ? ?# 總監(jiān)安排到2018分析日程中 ? ? finance_2018.add_work(adviser) ?# 顧問(wèn)安排到2018分析日程中 ? ? work.add_work(finance_2018) # 添加2018年財(cái)務(wù)工作安排 ? ? work.visit()
原文鏈接:https://is-cloud.blog.csdn.net/article/details/122934106
相關(guān)推薦
- 2022-08-02 Golang中panic與recover的區(qū)別_Golang
- 2022-03-11 Linux fatal error: iostream: No such file or direc
- 2022-12-07 c++的virtual和override作用及說(shuō)明_C 語(yǔ)言
- 2022-11-12 CSS單標(biāo)簽實(shí)現(xiàn)復(fù)雜的棋盤(pán)布局_經(jīng)驗(yàn)交流
- 2022-04-14 C#可變參數(shù)params示例詳解_C#教程
- 2022-04-22 electron設(shè)置最小大小和最大大小
- 2022-01-12 解決element-ui 日期選擇器提交后臺(tái)數(shù)據(jù)不準(zhǔn)確問(wèn)題
- 2022-03-17 分布式數(shù)據(jù)存儲(chǔ)系統(tǒng)的三要素_數(shù)據(jù)庫(kù)其它
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過(guò)濾器
- Spring Security概述快速入門(mén)
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支