設計模式之訪問者模式
剛剛學完設計模式的訪問者模式(編譯器模式),這裏就對該模式進行了總結與分析.
一.產生原因
這裏存在一個這樣的問題:如果某係統已經完成了一個類層次並提供了滿足需求的所有接口,現在要增加新的需求,我們需要怎麼做?
可能你會采用增加該需求並把整個層次結構統統修改一遍.然而如果需求變動會不停的發生,而且需求的任何變動都會讓整個結構統統修改一遍,此時你會怎麼做呢?
所以,我們現在需要對這個係統結構進行重構,訪問者模式也許就是你解決上麵問題最好的選擇.
那麼什麼是訪問者模式呢?
二.訪問者模式的定義與結構
模式定義
訪問者模式(Visitor Pattern):表示一個作用於某對象結構中的各元素的操作,它使我們可以在不改變各元素的類的前提下定義作用於這些元素的新操作。
訪問者模式是一種對象行為型模式。顧名思義(通俗定義)
使用這個模式後就可以在不修改已有程序結構的前提下,通過添加額外的“訪問者”完成對已有代碼功能的提升。下麵是一個很常見的訪問者模式結構圖:
把它簡化後就是這樣一張圖:
由圖可見,訪問者模式主要是由以下5部分角色組成:
Vistor: 抽象訪問者.定義接口,聲明一個或多個訪問操作.
ConcreteVisitor: 具體訪問者.實現抽象訪問者所聲明的接口,也就是抽象訪問者所聲明的各個訪問操作.
Element: 抽象元素.聲明一個接受操作,接受一個訪問者對象作為一個參數.
ConcreteElement:具體元素.實現抽象結點所規定的接受操作.
ObjectStructure:對象結構.可以遍曆結構中的所有元素,提供一個接口讓訪問者對象都可以訪問每一個元素.
三.實例分析
通過下麵的例子來分析該訪問者模式的概念.這是清華大學課件上的例子,還有很多例子:男人與女人、歡樂穀等,但個人覺得這個例子更適合闡述清楚該模式。
購物車
顧客在超市中將選擇的商品,如蘋果、圖書等放在購物車中,然後到收銀員處付款。在購物過程中,顧客需要對這些商品進行訪問,以便確認這些商品的質量,之後收銀員計算價格時也需要訪問購物車內顧客所選擇的商品。此時,購物車作為一個ObjectStructure(對象結構)用於存儲各種類型的商品,而顧客和收銀員作為訪問這些商品的訪問者,他們需要對商品進行檢查和計價。不同類型的商品其訪問形式也可能不同,如蘋果需要過秤之後再計價,而圖書不需要。使用訪問者模式來設計該購物過程。
它的訪問者模式結構圖為:
由圖可以知:在該結構體係中Apple和Book都是抽象的元素,是Product的具體實現。而抽象訪問者是Visitor,它的具體訪問者是Customer(顧客)和Saler(收銀員),它們之間的關係通過BuyBasket(購物車)這個具體的對象結構聯係,各自都有相關的方法.我嚐試用C#實現了該模式,如下:
首先是元素代碼部分:
然後是訪問者代碼部分:
然後是通過對象結構把元素和訪問者串聯在一起:
最後是在Main中實現,實例化具體的方法並輸出結果:
由此可見,通過訪問者能夠實現具體的訪問者顧客和售貨員都訪問到具體的元素蘋果和書.
四.優點
現在又遇到一個問題:
要向原來的購物車類層次增加一個新的操作——工商局的檢查超市產品的生產日期,怎麼解決呢?
解決方法:
僅僅需要添加一個具體的訪問者角色即可,而不必修改整個類層次,這樣就符合“開閉原則”的需求。
而且每個訪問者角色都對應一個相關的操作,當需要修改一個具體的訪問者角色時僅僅修改該訪問者角色,而不是修改整個類層次.
如下圖所示:添加紅色部分具體的訪問者business(工商局)即可實現該操作。
由此可見訪問者模式的優點如下:
1.增加新的訪問操作變得容易;
2.將有關元素對象的訪問行為集中到一個訪問者對象中,而不是分散到各個元素類中;
3.用戶能在不修改類層次結構情況下定義該類層次結構的操作並修改.
五.缺點
現在又遇到一個問題:要向原來的購物車類層次增加一個新的元素“Pen”,或者“Book”元素需要改變,怎麼解決呢?
解決方法:你必須要修改訪問者角色和每一個具體訪問者角色,對應的訪問者每一個函數的參數都要相應的變化.如下圖所示:修改"Book"時每個具體的訪問者和抽象的訪問者的方法都要修改,這就相當的麻煩了。
由此可見,訪問者模式的缺點如下:
缺點一:訪問者模式增加新的元素很困難,每增加一個新的元素或修改一個元素,就要對相應的訪問者類進行修改,違背了“開閉原則”。
而且訪問者角色要執行與角色相關的操作,就必須讓元素的內部屬性暴露出來,在java中就意味著對象可以訪問,這就破壞了元素的封裝性。
缺點二:破壞封裝性。
缺點三:而且訪問者之間能夠傳遞的信息有限,這就往往會限製訪問者模式的使用。
六.總結
這是一個巧妙而又複雜的模式,它的使用條件比較苛刻.
當係統存在固定的數據結構(類層次),又有不同的行為,那麼訪問者模式是個不錯的選擇.如果需要修改或增加元素時,就不應該使用訪問者模式.
訪問者模式要盡可能的將對象瀏覽邏輯存放在Visitor類中,而不是放在它的子類中;這樣ConcreteVisitor類所訪問的對象結構依賴較少,便於維護。
七.區別
由於該訪問者模式(又稱編譯器模式)是金福生老師讓我們與解釋器模式一起學習的,所以老師課堂上提出一個這樣的問題:
訪問者模式(編譯器模式)和解釋器模式有啥區別?
通俗的說就是編譯器模式(訪問者模式)就是相當於把所有的都編譯之後,能訪問到所有東西的模式;而解釋器模式是相當於編譯一條語句執行一條語句的模式.具體有啥區別沒有深入的分析,見諒.其中該博客的思想主要來自:自己的理 解與分析清華大學的訪問者模式課件
一些思想來自下麵三個博客的閱讀
https://blog.csdn.net/xiaoquanhuang/article/details/6311319
https://www.cnblogs.com/pursuedream/articles/795051.html
https://www.iteye.com/topic/345384
感謝上麵3個文章的作者和清華大學的課件,這是我的一些理解與分析.僅供大家學習,有不足之處見諒.(By:Eastmount 2013-3)
最後更新:2017-04-03 21:30:14