一、引言
今天我們開始講“行為型”設計模式的第九個模式,該模式是【通路者模式】,英文名稱是:Visitor Pattern。如果按老規矩,先從名稱上來看看這個模式,我根本不能獲得任何對了解該模式有用的資訊,而且這個模式在我們的編碼生活中使用的并不是很多。該模式的意圖定義很抽象,第一次看了這個定義其實和沒看沒有什麼差別,一頭霧水,為了讓大家更好的了解該模式的初衷,我們舉個例子來說明模式。比如:當我們為了解決一個新的軟體需求的時候,經過多個日以繼夜的努力,最終通過一個完美(自己認為的)的軟體設計解決了客戶提出的新的需求,而且這個設計有完美的類層次結構,并且是符合OO的設計原則的,我們很開心,對自己設計的東西很有成就感。又過了一段時間,客戶突然又有了一個新的需求,需要為現有的類層次結構裡面的類增加一個新的操作(其實就是一個方法),怎麼辦?好辦,在面向OO設計模式中有一個模式就是為了解決這個問題的,那就是“通路者模式”,可以為現有的類層次結構中的類輕松增加新的操作,我們繼續吧,好好的了解一下該模式。
二、通路者模式的詳細介紹
2.1、動機(Motivate)
在軟體建構過程中,由于需求的改變,某些類層次結構中常常需要增加新的行為(方法),如果直接在基類中做這樣的更改,将會給子類帶來很繁重的變更負擔,甚至破壞原有設計。如何在不更改類層次結構的前提下,在運作時根據需要透明地為類層次結構上的各個類動态添加新的操作,進而避免上述問題?
2.2、意圖(Intent)
表示一個作用于某對象結構中的各個元素的操作。它可以在不改變各元素的類的前提下定義作用于這些元素的新的操作。 ——《設計模式》GoF
2.3、結構圖(Structure)

2.4、模式的組成
可以看出,在通路者模式的結構圖有以下角色:
(1)、抽象通路者角色(Vistor): 聲明一個包括多個通路操作,多個操作針對多個具體節點角色(可以說有多少個具體節點角色就有多少通路操作),使得所有具體通路者必須實作的接口。
(2)、具體通路者角色(ConcreteVistor):實作抽象通路者角色中所有聲明的接口,也可以說是實作對每個具體節點角色的新的操作。
(3)、抽象節點角色(Element):聲明一個接受操作,接受一個通路者對象作為參數,如果有其他參數,可以在這個“接受操作”裡在定義相關的參數。
(4)、具體節點角色(ConcreteElement):實作抽象元素所規定的接受操作。
(5)、結構對象角色(ObjectStructure):節點的容器,可以包含多個不同類或接口的容器。
2.5、通路者模式的代碼實作
通路者這個模式在我們現實的編碼生活中使用的并不是很多,我就直接貼代碼,讓大家看代碼的結構吧。今天給大家兩個代碼執行個體,自己慢慢體會通路者吧。實作代碼如下:
這是通路者模式第二種代碼執行個體:
三、通路者模式的實作要點:
Visitor模式通過所謂雙重分發(double dispatch)來實作在不更改Element類層次結構的前提下,在運作時透明地為類層次結構上的各個類動态添加新的操作。所謂雙重分發即Visitor模式中間包括了兩個多态分發(注意其中的多态機制):第一個為accept方法的多态辨析;第二個為visit方法的多态辨析。
設計模式其實是一種堵漏洞的方式,但是沒有一種設計模式能夠堵完所有的漏洞,即使是組合各種設計模式也是一樣。每個設計模式都有漏洞,都有它們解決不了的情況或者變化。每一種設計模式都假定了某種變化,也假定了某種不變化。Visitor模式假定的就是操作變化,而Element類層次結構穩定。
(1)、通路者模式的主要優點有:
1】、通路者模式使得添加新的操作變得容易。如果一些操作依賴于一個複雜的結構對象的話,那麼一般而言,添加新的操作會變得很複雜。而使用通路者模式,增加新的操作就意味着添加一個新的通路者類。是以,使得添加新的操作變得容易。
2】、通路者模式使得有關的行為操作集中到一個通路者對象中,而不是分散到一個個的元素類中。這點類似與”中介者模式”。
3】、通路者模式可以通路屬于不同的等級結構的成員對象,而疊代隻能通路屬于同一個等級結構的成員對象。
(2)、通路者模式的主要缺點有:
1】、增加新的元素類變得困難。每增加一個新的元素意味着要在抽象通路者角色中增加一個新的抽象操作,并在每一個具體通路者類中添加相應的具體操作。具體來說,Visitor模式的最大缺點在于擴充類層次結構(增添新的Element子類),會導緻Visitor類的改變。是以Visitor模式适用于“Element類層次結構穩定,而其中的操作卻經常面臨頻繁改動”。
(3)、在下面的情況下可以考慮使用通路者模式:
1】、如果系統有比較穩定的資料結構,而又有易于變化的算法時,此時可以考慮使用通路者模式。因為通路者模式使得算法操作的添加比較容易。
2】、如果一組類中,存在着相似的操作,為了避免出現大量重複的代碼,可以考慮把重複的操作封裝到通路者中。(當然也可以考慮使用抽象類了)
3】、如果一個對象存在着一些與本身對象不相幹,或關系比較弱的操作時,為了避免操作污染這個對象,則可以考慮把這些操作封裝到通路者對象中。
四、.NET 通路者模式的實作
在現在的Net架構裡面,如果要想給現有的類增加新的方法,有了新的方式,那就是“擴充方法”,使用起來和執行個體方法是一樣一樣的,而且在Net架構裡面,微軟自己也寫了很多的擴充方法給我們使用。我目前還沒有學習到Net的架構類庫裡面有“通路者模式”實作,看來自己還需努力,革命尚未成功啊。
五、總結
通路者模式寫完了,這個模式剛開始了解起來還是挺麻煩的,但是,如果我們多看幾個執行個體代碼,完全掌握也不是問題。随着C#語言的發展,設計模式裡面的很多東西,我們可以通過C#語言的一些特性做更好的替代。我們寫設計模式剛開始要慢慢來,一步一步的照貓畫虎的來寫代碼,等我們熟練掌握了模式的核心意思,我們就要寫符合C#風格和特性的模式代碼了,或者說我們要用C#來寫設計模式了,寫出來的代碼會更棒。