1、 訪問者(Vistor)模式算法
訪問者模式是封裝一些施加於某種數據結構之上的操做。一旦這些操做須要修改的話,接受這個操做的數據結構則能夠保存不變。訪問者模式適用於數據結構相對穩定的系統, 它把數據結構和做用於數據結構之上的操做之間的耦合度下降,使得操做集合能夠相對自由地改變。設計模式
數據結構的每個節點均可以接受一個訪問者的調用,此節點向訪問者對象傳入節點對象,而訪問者對象則反過來執行節點對象的操做。這樣的過程叫作「雙重分派」。節點調用訪問者,將它本身傳入,訪問者則將某算法針對此節點執行。數據結構
2、 訪問者模式的結構dom
從上面描述可知,訪問者模式是用來封裝某種數據結構中的方法。具體封裝過程是:每一個元素接受一個訪問者的調用,每一個元素的Accept方法接受訪問者對象做爲參數傳入,訪問者對象則反過來調用元素對象的操做。具體的訪問者模式結構圖以下所示。ide
這裏須要明確一點:訪問者模式中具體訪問者的數目和具體節點的數目沒有任何關係。從訪問者的結構圖能夠看出,訪問者模式涉及如下幾類角色。this
3、 訪問者模式的實現spa
在講訴訪問者模式的實現時,我想先不用訪問者模式的方式來實現某個場景。具體場景是——如今我想遍歷每一個元素對象,而後調用每一個元素對象的Print方法來打印該元素對象的信息。若是此時不採用訪問者模式的話,實現這個場景再簡單不過了,具體實現代碼以下所示:設計
using System; using System.Collections; namespace VistorPattern { // 抽象元素角色 public abstract class Element { public abstract void Print(); } // 具體元素A public class ElementA : Element { public override void Print() { Console.WriteLine("我是元素A"); } } // 具體元素B public class ElementB : Element { public override void Print() { Console.WriteLine("我是元素B"); } } // 對象結構 public class ObjectStructure { private readonly ArrayList _elements = new ArrayList(); public ArrayList Elements { get { return _elements; } } public ObjectStructure() { var ran = new Random(); for (int i = 0; i < 6; i++) { int ranNum = ran.Next(10); if (ranNum > 5) { _elements.Add(new ElementA()); } else { _elements.Add(new ElementB()); } } } } class Program { static void Main(string[] args) { var objectStructure = new ObjectStructure(); // 遍歷對象結構中的對象集合,訪問每一個元素的Print方法打印元素信息 foreach (Element e in objectStructure.Elements) { e.Print(); } Console.Read(); } } }
上面代碼很準確瞭解決了咱們剛纔提出的場景,可是需求在時刻變化的,若是此時,我除了想打印元素的信息外,還想打印出元素被訪問的時間,此時咱們就不得不去修改每一個元素的Print方法,再加入相對應的輸入訪問時間的輸出信息。這樣的設計顯然不符合「開-閉」原則,即某個方法操做的改變,會使得必須去更改每一個元素類。既然,這裏變化的點是操做的改變,而每一個元素的數據結構是不變的。因此此時就思考——能不能把操做於元素的操做和元素自己的數據結構分開呢?解開這二者的耦合度,這樣若是是操做發現變化時,就不須要去更改元素自己了,可是若是是元素數據結構發現變化,例如,添加了某個字段,這樣就不得不去修改元素類了。此時,咱們可使用訪問者模式來解決這個問題,即把做用於具體元素的操做由訪問者對象來調用。具體的實現代碼以下所示:code
using System; using System.Collections; namespace VistorPattern { // 抽象元素角色 public abstract class Element { public abstract void Accept(IVistor vistor); public abstract void Print(); } // 具體元素A public class ElementA : Element { public override void Accept(IVistor vistor) { // 調用訪問者visit方法 vistor.Visit(this); } public override void Print() { Console.WriteLine("我是元素A"); } } // 具體元素B public class ElementB : Element { public override void Accept(IVistor vistor) { vistor.Visit(this); } public override void Print() { Console.WriteLine("我是元素B"); } } // 抽象訪問者 public interface IVistor { void Visit(ElementA a); void Visit(ElementB b); } // 具體訪問者 public class ConcreteVistor : IVistor { // visit方法而是再去調用元素的Accept方法 public void Visit(ElementA a) { a.Print(); } public void Visit(ElementB b) { b.Print(); } } // 對象結構 public class ObjectStructure { private readonly ArrayList _elements = new ArrayList(); public ArrayList Elements { get { return _elements; } } public ObjectStructure() { var ran = new Random(); for (int i = 0; i < 6; i++) { int ranNum = ran.Next(10); if (ranNum > 5) { _elements.Add(new ElementA()); } else { _elements.Add(new ElementB()); } } } } class Program { static void Main(string[] args) { var objectStructure = new ObjectStructure(); foreach (Element e in objectStructure.Elements) { // 每一個元素接受訪問者訪問 e.Accept(new ConcreteVistor()); } Console.Read(); } } }
從上面代碼可知,使用訪問者模式實現上面場景後,元素Print方法的訪問封裝到了訪問者對象中了(我以爲能夠把Print方法封裝到具體訪問者對象中。),此時客戶端與元素的Print方法就隔離開了。此時,若是須要添加打印訪問時間的需求時,此時只須要再添加一個具體的訪問者類便可。此時就不須要去修改元素中的Print()方法了。對象
4、 訪問者模式的應用場景
每一個設計模式都有其應當使用的狀況,那讓咱們看看訪問者模式具體應用場景。若是遇到如下場景,此時咱們能夠考慮使用訪問者模式。
5、 訪問者模式的優缺點
優勢:
缺點:
6、 總結
訪問者模式是用來封裝一些施加於某種數據結構之上的操做。它使得能夠在不改變元素自己的前提下增長做用於這些元素的新操做,訪問者模式的目的是把操做從數據結構中分離出來。