設計模式——訪問者模式

在閻宏博士的《JAVA與模式》一書中開頭是這樣描述訪問者(Visitor)模式的:訪問者模式是對象的行為模式。訪問者模式的目的是封裝一些施加於某種數據結構元素之上的操作。一旦這些操作需要修改的話,接受這個操作的數據結構則可以保持不變。

在進行訪問者模式的學習之前,我們先來了解下分派的概念。

設計模式——訪問者模式

分派

分派是什麼?

比如下面的例子:

設計模式——訪問者模式

在這裡,list的顯示類型就是List,而實際類型卻是ArrayList。

根據對象的類型而對方法進行的選擇,就是分派(Dispatch),分派(Dispatch)又分為兩種,即靜態分派和動態分派。

  • 靜態分派(Static Dispatch)發生在編譯時期,分派根據靜態類型信息發生。靜態分派對於我們來說並不陌生,方法重載就是靜態分派。

  • 動態分派(Dynamic Dispatch)發生在運行時期,動態分派動態地置換掉某個方法。方法重寫就是動態分派。

根據分派可以基於多少種宗量,可以將面向對象的語言劃分為單分派語言(Uni-Dispatch)和多分派語言(Multi-Dispatch)。單分派語言根據一個宗量的類型進行對方法的選擇,多分派語言根據多於一個的宗量的類型對方法進行選擇。

Java就是動態的單分派語言,因為這種語言的動態分派僅僅會考慮到方法的接收者的類型,同時又是靜態的多分派語言,因為這種語言對重載方法的分派會考慮到方法的接收者的類型以及方法的所有參數的類型。

在一個支持動態單分派的語言裡面,有兩個條件決定了一個請求會調用哪一個操作,第二個是請求的名字,而是接收者的真實類型。單分派限制了方法的選擇過程,使得只有一個宗量可以被考慮到,這個宗量通常就是方法的接收者。

訪問者模式

訪問者模式是一種較為複雜的行為型模式,主要由訪問者和被訪問者兩部分組成,通常被訪問的元素具有不同的類型屬性,一般不同的訪問者對它們的訪問側重點操作不同。所以訪問者模式的作用就是封裝一些作用於某種數據結構的元素操作,然後在不改變數據結構的前提下定義這些元素的新操作。

訪問者模式和裝飾者模式很相似,都是新增功能。裝飾模式更多的是實現對己有功能的加強、修改或者完全重新實現。而訪問者模式更多的是實現為對象結構添加新的功能。

訪問者模式結構

UML圖

設計模式——訪問者模式

訪問者模式的組成角色

  • 具體訪問者角色(ConcreteVisitor):具體訪問者實現了抽象訪問者方法的實現,定義出每個元素訪問時的具體行為。

  • 抽象元素角色(Element):一般是一個抽象類或接口,定義一個Accept方法,該方法通常以一個抽象訪問者作為參數。

  • 具體元素角色(ConcreteElement):具體元素實現了Accept方法,在Accept方法中調用訪問者的訪問方法以便提供具體的訪問實現。

  • 對象結構角色(ObjectStructure):對象結構是一個元素的集合,用於存放元素對象,且提供便利其內部元素的方法。

案例演示

訪問者模式是一個結構、概念較為複雜的模式,使用頻率不是很高,但是在特定場景下會體現出非常大的靈活性。

這裡我以【Android源碼設計模式】中的一個例子來演示下訪問者模式的使用。在我們的績效過評估中,CTO首席技術官只關注員工的成果數量,而CEO只需要瞭解員工的kpi值。在這種場景下,公司不同員工對應的領導需要訪問的側重點不同,這種情況下就可以使用訪問者模式。

定義抽象員工類

設計模式——訪問者模式

在抽象員工類中,我們定義了共有的屬性:姓名、kpi,同時定義了accept接口用來接收訪問者。

設計模式——訪問者模式

在上面,我們定義了軟件開發工程師和產品經理兩種角色,同時兩種角色的側重點不同,有代碼量和產品設計量。

設計模式——訪問者模式

設計模式——訪問者模式

設計模式——訪問者模式

客戶端

設計模式——訪問者模式

結果

設計模式——訪問者模式

訪問者模式優缺點

優點

  • 良好的擴展性,能夠在不修改對象結構的元素下,為結構中的對象添加新功能;

  • 良好的複用性,通過訪問者來定義整個對象結構通用的功能,從而提高複用程序;

  • 分離無關行為。可以通過訪問者分離無關的行為,把相關的行為封裝在一起,構成一個訪問者,這樣每一個訪問者的功能都比較單一。

缺點

  • 對象結構變化很困難。在於增添新的Element子類的時候會導致Visitor類發生改變,而且隨著Element子類的增加,Visitor類會越來越龐大。

  • 破壞封裝。訪問者模式通常需要對象結構開放內部數據給訪問者和ObjectStructure,這破壞了對象的封裝性。

二次分派技術

一個方法根據兩個宗量的類型來決定執行不同的代碼,這就是“雙重分派”。Java語言不支持動態的多分派,也就意味著Java不支持動態的雙分派。但是通過使用設計模式,也可以在Java語言裡實現動態的雙重分派。

兩次分派技術使客戶端的請求不再被靜態地綁定在元素對象上,這個時候真正執行什麼樣的功能同時取決於訪問者類型和元素的類型,就算同一種元素類型,只要訪問者的類型不同,最終執行功能也會不一樣。這樣一來,就可以在元素對象不變的情況下,通過改變訪問者類型來改變真正執行的功能。


分享到:


相關文章: