行為型模式--Visitor(通路者)對象行為型模式
一. 意圖
表示一個作用于某對象結構中的各元素的操作. 它使你可以在不改變各元素的類的前提下定義作用于這些元素的新操作..
使用Visitor模式, 必須定義兩個類層次: 一個對應于接受操作的元素層次, 另一個對應于定義對元素的操作的通路者層次(元素通路者). 給通路者類層次增加一個新的子類即可建立一個新的操作.
二. 适用性
1. 一個對象結構包含很多類對象, 它們有不同的接口, 而你想對這些對象實施一些依賴于其具體類的操作.
2. 需要對一個對象結構中的對象進行很多不同的并且不相關的操作, 而你想避免讓這些操作"污染"這些對象的類. Visitor使得你可以将相關的操作集中起來定義在一個類中. 當該對象結構被很多應用共享時, 用Visitor模式讓每個應用僅包含需要用到的操作.
3. 定義對象結構的類很少改變, 但經常需要在此結構上定義新的操作. 改變對象結構類需要重定義對所有通路者的接口, 這可能需要很大的代價. 如果對象結構類經常改變, 那麼可能還是在這些類中定義這些操作較好.
三. 模式結構

圖1
四. 角色說明
Visitor(通路者)
—為該對象結構中ConcreteElement的每一個類聲明一個Visit操作. 該操作的名字和特征辨別了發送Visit請求給該通路者的那個類. 這使得通路者可以确定正被通路元素的具體的類. 這樣通路者就可以通過該元素的特定接口直接通路它.
ConcreteVisitor(具體通路者)
—實作每個由Visitor聲明的操作. 每個操作實作本算法的一部分, 而該算法片斷乃是對應于結構中對象的類. ConcreteVisitor為該算法提供了上下文并存儲它的局部狀态. 這一狀态常常在周遊該結構的過程中累積結果.
Element(元素)
—定義一個Accept操作, 它以一個通路者為參數.
ConcreteElement(具體元素)
—實作Accept操作, 該操作以一個通路者為參數.
ObjectStructure(對象結構)
—能枚舉它的元素.
—可以提供一個高層的接口以允許該通路者通路它的元素.
—可以是一個複合(Composite)或是一個集合, 如一個清單或一個無序集合.
協作
圖2
一個使用Visitor模式的客戶必須建立一個ConcreteVisitor對象, 然後周遊該對象結構, 并用該通路者通路每一個元素.
當一個元素被通路時, 它調用對應于它的類的Visitor操作. 如果必要, 該元素将自身作為這個操作的一個參數以便該通路者通路它的狀态.
五. 說明
假設ObjectStruct對象中有ConcreteElementA對象和ConcreteElementB對象.
1. 通路者模式使得易于增加新的操作 通路者使得增加依賴于複雜對象結構的構件的操作變得容易了. 僅需增加一個新的通路者即可在一個對象結構上定義一個新的操作. 相反, 如果每個功能都分散在多個類之上的話, 定義新的操作時必須修改每一類.
(例如現在要在ObjectStruct 對象上增加新的操作, 如果不使用Visitor模式的話, 那就需要修改ConcreteElementA類和ConcreteElementB類, 而使用Visitor模式的話, 隻需要增加一個具體Visitor類, 并且在這個具體Visitor類中對應的方法VisitConcreteElementA(ConcreteElementA)和VisitConcreteElementB(ConcreteElementB)中增加相關的操作)(實踐證明, 在原有代碼上進行需求修改的話, 通常情況下, 增加代碼付出的代價要比修改代碼付出的代價要小)
2. 通路者集中相關的操作而分離無關的操作 相關的行為不是分布在定義該對象結構的各個類上, 而是集中在一個通路者中. 無關行為卻被分别放在它們各自的通路者子類中. 這就既簡化了這些元素的類, 也簡化了在這些通路者中定義的算法. 所有與它的算法相關的資料結構都可以被隐藏在通路者中.
3. 增加新的ConcreteElement類很困難 Visitor模式使得難以增加新的Element的子類. 每添加一個新的ConcreteElement都要在Vistor中添加一個新的抽象操作, 并在每一個ConcretVisitor類中實作相應的操作. 是以在應用通路者模式時考慮關鍵的問題是系統的哪個部分會經常變化, 是作用于對象結構上的算法呢還是構成該結構的各個對象的類. 如果老是有新的ConcretElement類加入進來的話, Vistor類層次将變得難以維護. 在這種情況下, 直接在構成該結構的類中定義這些操作可能更容易一些. 如果Element類層次是穩定的, 而你不斷地增加操作獲修改算法, 通路者模式可以幫助你管理這些改動.
(在Visitor層次上, 如果你是橫向修改的話(也就是增加具體Visitor)是簡單的, 而如果是縱向修改的話(也就是增加具體Element)是困難的).
4. 通過類層次進行通路一個疊代器(Iterator模式)可以通過調用節點對象的特定操作來周遊整個對象結構, 同時通路這些對象. 但是疊代器不能對具有不同元素類型的對象結構進行操作.
5. 累積狀态 當通路者通路對象結構中的每一個元素時, 它可能會累積狀态. 如果沒有通路者, 這一狀态将作為額外的參數傳遞給進行周遊的操作, 或者定義為全局變量.
6. 破壞封裝 通路者方法假定ConcreteElement接口的功能足夠強, 足以讓通路者進行它們的工作. 結果是, 該模式常常迫使你提供通路元素内部狀态的公共操作, 這可能會破壞它的封裝性.
六. 相關模式
通路者可以用于對一個由Composite模式定義的對象結構進行操作. Interpreter通路者可以用于解釋.