設計模式-行為型-訪問者模式
訪問者模式(Vistor):
訪問者模式的官方定義是這樣的:表示一個作用於某物件結構中的各元素的操作,它使你可以在不改變各元素類的前提下定義作用於這些元素的新操作。官方的東西總是晦澀難懂的,那麼我們現在就來拆解一下:首先"一個作用於某物件結構中的各元素的操作",提到了三個東西:物件結構、元素、操作。我們都學習過資料結構,資料結構中大家對資料的訪問一般都是直接訪問其地址。在面向物件的設計中,我們一般也是將資料的訪問操作放在類的內部,便於訪問。這種設計看似沒有什麼問題,但當我們想要採用不同方式訪問資料或物件結構時就必須要對類進行修改,這樣就違反了OCP原則。於是大家會想到將資料結構與操作分離開來,當問們需要新增訪問操作的時候直接新增新的類,原來的程式碼不需要做任何改變,這也是後半句提到的"可以在不改變各元素類的前提下定義作用於這些元素的新操作"。
訪問者模式的角色:
1)Visitor:介面或抽象類,定義了對每個Element訪問的行為,它的引數就是被訪問的元素,它的方法個數理論上與元素的個數是一樣的,因此,訪問者模式要求元素的型別要穩定,如果經常新增、移除元素類,必然會導致頻繁地修改visitor介面,如果出現這種情況,則說明不適合使用該模式。
2)ConcreteVisitor:具體的訪問者,它需要給出對每一個元素類訪問時所產生的具體行為。
3)Element:元素介面或抽象類,它定義了一個接受訪問者的方法(accept),其意思就是說每一個元素都可以被訪問者訪問。
4)ConcreteElement:具體的元素類,它提供接受訪問的具體實現,而這個具體實現通常情況下是使用訪問者提供的訪問該元素類的方法。
5)ObjectStructure:定義當中所提到的物件結構,物件結構是一個抽象表述,它內部管理了元素集合,並且可以迭代這些元素提供訪問者訪問。
示例:
一個目錄下面有資料夾和檔案,資料夾和檔案是具體的元素類。
1 /// <summary> 2 /// 抽象訪問者 3 /// </summary> 4 public abstract class Visitor 5 { 6 public abstract void visit(File file); 7 8 public abstract void visit(Directory directory); 9 } 10 11 /// <summary> 12 /// 具體的訪問者 13 /// </summary> 14 public class ListVisitor : Visitor 15 { 16 public override void visit(File file) 17 { 18 Console.WriteLine($"檔名稱{file.GetName()}"); 19 } 20 21 public override void visit(Directory directory) 22 { 23 Console.WriteLine($"資料夾名稱{directory.GetName()}"); 24 } 25 } 26 27 /// <summary> 28 /// 元素抽象類 29 /// </summary> 30 public abstract class Element 31 { 32 public abstract void accept(Visitor visitor); 33 } 34 35 public class Entry : Element 36 { 37 private string name; 38 39 public Entry(string name) 40 { 41 this.name = name; 42 } 43 44 public string GetName() 45 { 46 return this.name; 47 } 48 49 public override void accept(Visitor visitor) 50 { 51 throw new NotImplementedException(); 52 } 53 } 54 55 /// <summary> 56 /// 具體的元素類:資料夾 57 /// </summary> 58 public class Directory : Entry 59 { 60 public Directory(string name) 61 : base(name) 62 { 63 } 64 65 public override void accept(Visitor visitor) 66 { 67 visitor.visit(this); 68 } 69 } 70 71 /// <summary> 72 /// 具體的元素類:檔案 73 /// </summary> 74 public class File : Entry 75 { 76 public File(string name) 77 : base(name) 78 { 79 } 80 81 public override void accept(Visitor visitor) 82 { 83 visitor.visit(this); 84 } 85 }
定義好了,資料操作,接下來我們定義資料結構的核心類ObjectStructure。
1 public class ObjectStructure 2 { 3 private readonly Visitor visitor; 4 private List<Element> list = new List<Element>(); 5 6 public ObjectStructure(Visitor visitor) 7 { 8 this.visitor = visitor; 9 } 10 11 public void Add(Element element) 12 { 13 list.Add(element); 14 } 15 16 public void Show() 17 { 18 foreach (Element element in list) 19 { 20 element.accept(visitor); 21 } 22 } 23 }
客戶端呼叫。
1 private static void Main(string[] args) 2 { 3 ObjectStructure objectStructure = new ObjectStructure(new ListVisitor()); 4 objectStructure.Add(new Directory("我的資料夾")); 5 objectStructure.Add(new File("C#高階程式設計.pdf")); 6 objectStructure.Add(new File("設計模式.pdf")); 7 objectStructure.Add(new File("演算法.txt")); 8 objectStructure.Add(new File("mm.png")); 9 objectStructure.Show(); 10 }
訪問者模式的優缺點:
優點:
1)各角色職責分離,符合單一職責原則。
2)具有優秀的擴充套件性,使得資料結構和作用於結構上的操作解耦,使得操作集合可以獨立變化。
3)靈活性
缺點:
1)具體元素對訪問者公佈細節,違反了迪米特原則。
2)具體元素變更比較困難。
3)違反了依賴倒置原則,依賴了具體類,沒有依賴抽象。
訪問者模式的使用場景:
1)物件結構中物件對應的類很少改變,但經常需要在此物件結構上定義新的操作。
2)需要對一個物件結構中的物件進行很多不同的並且不相關的操作,而需要避免讓這些操作"汙染"這些物件的類,也不希望在增加新操作時修改這些類。
參考:https://www.cnblogs.com/zyrblog/p/9244754.