訪問者模式(Visitor),表示一個做用於某對象結構中的各元素的操做。它使你能夠在不改變各元素的類的前提下定義做用於這些元素的新操做。javascript
下面給出訪問者模式的結構圖:html
從類圖能夠看出,訪問者模式那是至關複雜啊!java
下面給出訪問者模式的基本代碼結構:git
namespace ConsoleApplication1 { //Visitor類,爲該對象結構中ConcreteElement的每個類聲明一個Visit操做。 abstract class Visitor { public abstract void VisitConcreteElementA(ConcreteElementA concreteElementA); public abstract void VisitConcreteElementB(ConcreteElementB concreteElementB); } //ConcreteVisitor1和ConcreteVisitor2類,具體訪問者,實現每一個由Visitor聲明的操做。每一個操做實現算法的一部分 //而該算法片斷乃是對應結構中對象的類 class ConcreteVisitor1 : Visitor { public override void VisitConcreteElementA(ConcreteElementA concreteElementA) { Console.WriteLine("{0}被{1}訪問",concreteElementA.GetType().Name,this.GetType().Name); } public override void VisitConcreteElementB(ConcreteElementB concreteElementB) { Console.WriteLine("{0}被{1}訪問",concreteElementB.GetType().Name,this.GetType().Name); } } class ConcreteVisitor2 : Visitor { public override void VisitConcreteElementA(ConcreteElementA concreteElementA) { Console.WriteLine("{0}被{1}訪問",concreteElementA.GetType().Name,this.GetType().Name); } public override void VisitConcreteElementB(ConcreteElementB concreteElementB) { Console.WriteLine("{0}被{1}訪問",concreteElementB.GetType().Name,this.GetType().Name); } } //Element類,定義一個Accepr操做,它以一個訪問者爲參數 abstract class Element { public abstract void Accept(Visitor visitor); } //ConcreteElementA類和ConcreteElementB類,具體元素,實現Accept類 class ConcreteElementA : Element { public override void Accept(Visitor visitor) { visitor.VisitConcreteElementA(this); } public void OperationA() {} } class ConcreteElementB : Element { public override void Accept(Visitor visitor) { visitor.VisitConcreteElementB(this); } public void OperationB() {} } //ObjectStructure類,能枚舉它的元素,能夠提供一個高層的接口以容許訪問者訪問它的元素 class ObjectStructure { private IList<Element> elements = new List<Element>(); public void Attach(Element element) { elements.Add(element); } public void Detach(Element element) { elements.Remove(element); } public void Accept(Visitor visitor) { foreach(Element e in elements) { e.Accept(visitor); } } } class Program { static void Main(string[] args) { ObjectStructure o = new ObjectStructure(); o.Attach(new ConcreteElementA()); o.Attach(new ConcreteElementB()); ConcreteVisitor1 v1 = new ConcreteVisitor1(); ConcreteVisitor2 v2 = new ConcreteVisitor2(); o.Accept(v1); o.Accept(v2); Console.ReadKey(); } } }
輸出結果以下所示:算法
訪問者模式適用於數據結構相對穩定的系統,它把數據結構和做用於結構上的操做之間的耦合解脫開,使得操做集合能夠相對自由地演化。設計模式
訪問者模式的目的是要把處理從數據結構分離出來。不少系統能夠按照算法和數據結構分開,若是這樣的系統有比較穩定的數據結構,又有易於變化的算法的話,使用訪問者模式就是比較適合的,由於訪問者模式使得算法操做的增長變得容易。反之,若是這樣的系統的數據結構對象易於變化,常常要有新的數據對象增長進來,,就不適合使用訪問者模式。數據結構
訪問者模式的有點就是增長新的操做很容易,由於增長新的操做意味着增長一個新的訪問者。訪問者模式將有關的行爲集中到一個訪問者對象中。ide
訪問者模式的缺點就是使增長新的數據結構變得困難了。post
要特別注意,只要數據結構相對穩定的系統才適合用訪問者模式,例如《大話設計模式》裏面的男人和女人的例子,只有男人或女人。學習
namespace ConsoleApplication1 { abstract class Action { //獲得男人的結論或反應 public abstract void GetManConclusion(Man concreteElementA); //獲得女人結論或反應 public abstract void GetWomanConclusion(Woman concreteElementB); } abstract class Person { //接受 public abstract void Accept(Action visitor); } //成功 具體"狀態"類 class Success : Action { public override void GetManConclusion(Man concreteElementA) { Console.WriteLine("{0}{1}時,背後多半有一個偉大的女人。", concreteElementA.GetType().Name, this.GetType().Name); } public override void GetWomanConclusion(Woman concreteElementB) { Console.WriteLine("{0}{1}時,背後大多有一個不成功的男人。", concreteElementB.GetType().Name, this.GetType().Name); } } //失敗 具體"狀態"類 class Failing : Action { public override void GetManConclusion(Man concreteElementA) { Console.WriteLine("{0}{1}時,悶頭喝酒,誰也不用勸。", concreteElementA.GetType().Name, this.GetType().Name); } public override void GetWomanConclusion(Woman concreteElementB) { Console.WriteLine("{0}{1}時,眼淚汪汪,誰也勸不了。", concreteElementB.GetType().Name, this.GetType().Name); } } //戀愛 具體"狀態"類 class Amativeness : Action { public override void GetManConclusion(Man concreteElementA) { Console.WriteLine("{0}{1}時,凡是不懂,也要裝懂。", concreteElementA.GetType().Name, this.GetType().Name); } public override void GetWomanConclusion(Woman concreteElementB) { Console.WriteLine("{0}{1}時,遇事懂也裝不懂。", concreteElementB.GetType().Name, this.GetType().Name); } } //男人類 class Man : Person { public override void Accept(Action visitor) { visitor.GetManConclusion(this); //首先客戶程序中將具體的狀態做爲參數傳遞給"男人"類完成了一次分派,而後"男人"類調用做爲參數的"具體狀態" //中的方法,"男人反應",同時將本身(this)做爲參數傳遞進去。這便完成了第二次分派 } } //女人類 class Woman : Person { public override void Accept(Action visitor) { visitor.GetWomanConclusion(this); } } //對象結構 class ObjectStructure { private IList<Person> elements = new List<Person>(); //增長 public void Attach(Person element) { elements.Add(element); } //移除 public void Detach(Person element) { elements.Remove(element); } //查看顯示 public void Display(Action visitor) { foreach(Person p in elements) { p.Accept(visitor); } } } class Program { static void Main(string[] args) { ObjectStructure o = new ObjectStructure(); o.Attach(new Man()); o.Attach(new Woman()); //成功時的反應 Success v1 = new Success(); o.Display(v1); //失敗時的反應 Failing v2 = new Failing(); o.Display(v2); //戀愛時的反應 Amativeness v3 = new Amativeness(); o.Display(v3); Console.ReadKey(); } } }
結果以下圖所示:
這樣作有什麼好處呢?
若是要增長一個結婚狀態來考察男人和女人的反應,因爲用了雙分派,只需增長一個「狀態」子類,就能夠在客戶端調用來查看,不須要改動其餘任何類的代碼。
//結婚狀態類 class Marriage : Action { public override void GetManConclusion(Man concreteElementA) { Console.WriteLine("{0}{1}時,感慨到:戀愛遊戲終結時,'有妻徒刑'遙無期", concreteElementA.GetType().Name, this.GetType().Name); } public override void GetWomanConclusion(Woman concreteElementB) { Console.WriteLine("{0}{1}時,欣慰曰:愛情長跑路漫漫,婚姻保險保平安", concreteElementB.GetType().Name, this.GetType().Name); } }
客戶端添加:
//客戶端添加 Marriage v4 = new Marriage(); o.Display(v4);