1、引言
今天咱們開始講「行爲型」設計模式的第九個模式,該模式是【訪問者模式】,英文名稱是:Visitor Pattern。若是按老規矩,先從名稱上來看看這個模式,我根本不能得到任何對理解該模式有用的信息,並且這個模式在咱們的編碼生活中使用的並非不少。該模式的意圖定義很抽象,第一次看了這個定義其實和沒看沒有什麼區別,一頭霧水,爲了讓你們更好的理解該模式的初衷,咱們舉個例子來講明模式。好比:當咱們爲了解決一個新的軟件需求的時候,通過多個日以繼夜的努力,最終經過一個完美(本身認爲的)的軟件設計解決了客戶提出的新的需求,並且這個設計有完美的類層次結構,而且是符合OO的設計原則的,咱們很開心,對本身設計的東西頗有成就感。又過了一段時間,客戶忽然又有了一個新的需求,須要爲現有的類層次結構裏面的類增長一個新的操做(其實就是一個方法),怎麼辦?好辦,在面向OO設計模式中有一個模式就是爲了解決這個問題的,那就是「訪問者模式」,能夠爲現有的類層次結構中的類輕鬆增長新的操做,咱們繼續吧,好好的瞭解一下該模式。
2、訪問者模式的詳細介紹
2.一、動機(Motivate)
在軟件構建過程當中,因爲需求的改變,某些類層次結構中經常須要增長新的行爲(方法),若是直接在基類中作這樣的更改,將會給子類帶來很繁重的變動負擔,甚至破壞原有設計。如何在不更改類層次結構的前提下,在運行時根據須要透明地爲類層次結構上的各個類動態添加新的操做,從而避免上述問題?
2.二、意圖(Intent)
表示一個做用於某對象結構中的各個元素的操做。它能夠在不改變各元素的類的前提下定義做用於這些元素的新的操做。 ——《設計模式》GoF
2.三、結構圖(Structure)
2.四、模式的組成
能夠看出,在訪問者模式的結構圖有如下角色:
(1)、抽象訪問者角色(Vistor): 聲明一個包括多個訪問操做,多個操做針對多個具體節點角色(能夠說有多少個具體節點角色就有多少訪問操做),使得全部具體訪問者必須實現的接口。
(2)、具體訪問者角色(ConcreteVistor):實現抽象訪問者角色中全部聲明的接口,也能夠說是實現對每一個具體節點角色的新的操做。
(3)、抽象節點角色(Element):聲明一個接受操做,接受一個訪問者對象做爲參數,若是有其餘參數,能夠在這個「接受操做」裏在定義相關的參數。
(4)、具體節點角色(ConcreteElement):實現抽象元素所規定的接受操做。
(5)、結構對象角色(ObjectStructure):節點的容器,能夠包含多個不一樣類或接口的容器。
2.五、訪問者模式的代碼實現
訪問者這個模式在咱們現實的編碼生活中使用的並非不少,我就直接貼代碼,讓你們看代碼的結構吧。今天給你們兩個代碼實例,本身慢慢體會訪問者吧。實現代碼以下:html
1 namespace Vistor 2 { 3 //抽象圖形定義---至關於「抽象節點角色」Element 4 public abstract class Shape 5 { 6 //畫圖形 7 public abstract void Draw(); 8 //外界注入具體訪問者 9 public abstract void Accept(ShapeVisitor visitor); 10 } 11 12 //抽象訪問者 Visitor 13 public abstract class ShapeVisitor 14 { 15 public abstract void Visit(Rectangle shape); 16 17 public abstract void Visit(Circle shape); 18 19 public abstract void Visit(Line shape); 20 21 //這裏有一點要說:Visit方法的參數能夠寫成Shape嗎?就是這樣 Visit(Shape shape),固然能夠,可是ShapeVisitor子類Visit方法就須要判斷當前的Shape是什麼類型,是Rectangle類型,是Circle類型,或者是Line類型。 22 } 23 24 //具體訪問者 ConcreteVisitor 25 public sealed class CustomVisitor : ShapeVisitor 26 { 27 //針對Rectangle對象 28 public override void Visit(Rectangle shape) 29 { 30 Console.WriteLine("針對Rectangle新的操做!"); 31 } 32 //針對Circle對象 33 public override void Visit(Circle shape) 34 { 35 Console.WriteLine("針對Circle新的操做!"); 36 } 37 //針對Line對象 38 public override void Visit(Line shape) 39 { 40 Console.WriteLine("針對Line新的操做!"); 41 } 42 } 43 44 //矩形----至關於「具體節點角色」 ConcreteElement 45 public sealed class Rectangle : Shape 46 { 47 public override void Draw() 48 { 49 Console.WriteLine("矩形我已經畫好!"); 50 } 51 52 public override void Accept(ShapeVisitor visitor) 53 { 54 visitor.Visit(this); 55 } 56 } 57 58 //圓形---至關於「具體節點角色」ConcreteElement 59 public sealed class Circle : Shape 60 { 61 public override void Draw() 62 { 63 Console.WriteLine("圓形我已經畫好!"); 64 } 65 66 public override void Accept(ShapeVisitor visitor) 67 { 68 visitor.Visit(this); 69 } 70 } 71 72 //直線---至關於「具體節點角色」 ConcreteElement 73 public sealed class Line : Shape 74 { 75 public override void Draw() 76 { 77 Console.WriteLine("直線我已經畫好!"); 78 } 79 80 public override void Accept(ShapeVisitor visitor) 81 { 82 visitor.Visit(this); 83 } 84 } 85 86 //結構對象角色 87 internal class AppStructure 88 { 89 private ShapeVisitor _visitor; 90 91 public AppStructure(ShapeVisitor visitor) 92 { 93 this._visitor = visitor; 94 } 95 96 public void Process(Shape shape) 97 { 98 shape.Accept(_visitor); 99 } 100 } 101 102 class Program 103 { 104 static void Main(string[] args) 105 { 106 //若是想執行新增長的操做 107 ShapeVisitor visitor = new CustomVisitor(); 108 AppStructure app = new AppStructure(visitor); 109 110 Shape shape = new Rectangle(); 111 shape.Draw();//執行本身的操做 112 app.Process(shape);//執行新的操做 113 114 115 shape = new Circle(); 116 shape.Draw();//執行本身的操做 117 app.Process(shape);//執行新的操做 118 119 120 shape = new Line(); 121 shape.Draw();//執行本身的操做 122 app.Process(shape);//執行新的操做 123 124 125 Console.ReadLine(); 126 } 127 } 128 }
這是訪問者模式第二種代碼實例:算法
1 namespace Visitor 2 { 3 //抽象訪問者角色 Visitor 4 public abstract class Visitor 5 { 6 public abstract void PutTelevision(Television tv); 7 8 public abstract void PutComputer(Computer comp); 9 } 10 11 //具體訪問者角色 ConcreteVisitor 12 public sealed class SizeVisitor : Visitor 13 { 14 public override void PutTelevision(Television tv) 15 { 16 Console.WriteLine("按商品大小{0}排放", tv.Size); 17 } 18 19 public override void PutComputer(Computer comp) 20 { 21 Console.WriteLine("按商品大小{0}排放", comp.Size); 22 } 23 } 24 25 //具體訪問者角色 ConcreteVisitor 26 public sealed class StateVisitor : Visitor 27 { 28 public override void PutTelevision(Television tv) 29 { 30 Console.WriteLine("按商品新舊值{0}排放", tv.State); 31 } 32 33 public override void PutComputer(Computer comp) 34 { 35 Console.WriteLine("按商品新舊值{0}排放", comp.State); 36 } 37 } 38 39 //抽象節點角色 Element 40 public abstract class Goods 41 { 42 public abstract void Operate(Visitor visitor); 43 44 private int nSize; 45 public int Size 46 { 47 get { return nSize; } 48 set { nSize = value; } 49 } 50 51 private int nState; 52 public int State 53 { 54 get { return nState; } 55 set { nState = value; } 56 } 57 } 58 59 //具體節點角色 ConcreteElement 60 public sealed class Television : Goods 61 { 62 public override void Operate(Visitor visitor) 63 { 64 visitor.PutTelevision(this); 65 } 66 } 67 68 //具體節點角色 ConcreteElement 69 public sealed class Computer : Goods 70 { 71 public override void Operate(Visitor visitor) 72 { 73 visitor.PutComputer(this); 74 } 75 } 76 77 //結構對象角色 78 public sealed class StoragePlatform 79 { 80 private IList<Goods> list = new List<Goods>(); 81 82 public void Attach(Goods element) 83 { 84 list.Add(element); 85 } 86 87 public void Detach(Goods element) 88 { 89 list.Remove(element); 90 } 91 92 public void Operate(Visitor visitor) 93 { 94 foreach (Goods g in list) 95 { 96 g.Operate(visitor); 97 } 98 } 99 } 100 101 class Program 102 { 103 static void Main(string[] args) 104 { 105 StoragePlatform platform = new StoragePlatform(); 106 platform.Attach(new Television()); 107 platform.Attach(new Computer()); 108 109 SizeVisitor sizeVisitor = new SizeVisitor(); 110 StateVisitor stateVisitor = new StateVisitor(); 111 112 platform.Operate(sizeVisitor); 113 platform.Operate(stateVisitor); 114 115 Console.Read(); 116 } 117 } 118 }
3、訪問者模式的實現要點:
Visitor模式經過所謂雙重分發(double dispatch)來實如今不更改Element類層次結構的前提下,在運行時透明地爲類層次結構上的各個類動態添加新的操做。所謂雙重分發即Visitor模式中間包括了兩個多態分發(注意其中的多態機制):第一個爲accept方法的多態辨析;第二個爲visit方法的多態辨析。
設計模式實際上是一種堵漏洞的方式,可是沒有一種設計模式可以堵完全部的漏洞,即便是組合各類設計模式也是同樣。每一個設計模式都有漏洞,都有它們解決不了的狀況或者變化。每一種設計模式都假定了某種變化,也假定了某種不變化。Visitor模式假定的就是操做變化,而Element類層次結構穩定。
(1)、訪問者模式的主要優勢有:
1】、訪問者模式使得添加新的操做變得容易。若是一些操做依賴於一個複雜的結構對象的話,那麼通常而言,添加新的操做會變得很複雜。而使用訪問者模式,增長新的操做就意味着添加一個新的訪問者類。所以,使得添加新的操做變得容易。
2】、訪問者模式使得有關的行爲操做集中到一個訪問者對象中,而不是分散到一個個的元素類中。這點相似與」中介者模式」。
3】、訪問者模式能夠訪問屬於不一樣的等級結構的成員對象,而迭代只能訪問屬於同一個等級結構的成員對象。
(2)、訪問者模式的主要缺點有:
1】、增長新的元素類變得困難。每增長一個新的元素意味着要在抽象訪問者角色中增長一個新的抽象操做,並在每個具體訪問者類中添加相應的具體操做。具體來講,Visitor模式的最大缺點在於擴展類層次結構(增添新的Element子類),會致使Visitor類的改變。所以Visitor模式適用於「Element類層次結構穩定,而其中的操做卻常常面臨頻繁改動」。
(3)、在下面的狀況下能夠考慮使用訪問者模式:
1】、若是系統有比較穩定的數據結構,而又有易於變化的算法時,此時能夠考慮使用訪問者模式。由於訪問者模式使得算法操做的添加比較容易。
2】、若是一組類中,存在着類似的操做,爲了不出現大量重複的代碼,能夠考慮把重複的操做封裝到訪問者中。(固然也能夠考慮使用抽象類了)
3】、若是一個對象存在着一些與自己對象不相干,或關係比較弱的操做時,爲了不操做污染這個對象,則能夠考慮把這些操做封裝到訪問者對象中。
4、.NET 訪問者模式的實現
在如今的Net框架裏面,若是要想給現有的類增長新的方法,有了新的方式,那就是「擴展方法」,使用起來和實例方法是同樣同樣的,並且在Net框架裏面,微軟本身也寫了不少的擴展方法給咱們使用。我目前尚未學習到Net的框架類庫裏面有「訪問者模式」實現,看來本身還需努力,革命還沒有成功啊。
5、總結
訪問者模式寫完了,這個模式剛開始理解起來仍是挺麻煩的,可是,若是咱們多看幾個實例代碼,徹底掌握也不是問題。隨着C#語言的發展,設計模式裏面的不少東西,咱們能夠經過C#語言的一些特性作更好的替代。咱們寫設計模式剛開始要慢慢來,一步一步的照貓畫虎的來寫代碼,等咱們熟練掌握了模式的核心意思,咱們就要寫符合C#風格和特性的模式代碼了,或者說咱們要用C#來寫設計模式了,寫出來的代碼會更棒。設計模式