.Net行為型設(shè)計(jì)模式之訪問者模式(Visitor)
一、動(dòng)機(jī)(Motivate)
在軟件構(gòu)建過程中,由于需求的改變,某些類層次結(jié)構(gòu)中常常需要增加新的行為(方法),如果直接在基類中做這樣的更改,將會(huì)給子類帶來很繁重的變更負(fù)擔(dān),甚至破壞原有設(shè)計(jì)。如何在不更改類層次結(jié)構(gòu)的前提下,在運(yùn)行時(shí)根據(jù)需要透明地為類層次結(jié)構(gòu)上的各個(gè)類動(dòng)態(tài)添加新的操作,從而避免上述問題?
二、意圖(Intent)
表示一個(gè)作用于某對(duì)象結(jié)構(gòu)中的各個(gè)元素的操作。它可以在不改變各元素的類的前提下定義作用于這些元素的新的操作。 ——《設(shè)計(jì)模式》GoF
三、結(jié)構(gòu)圖(Structure)
四、模式的組成
可以看出,在訪問者模式的結(jié)構(gòu)圖有以下角色:
(1)、抽象訪問者角色(Vistor): 聲明一個(gè)包括多個(gè)訪問操作,多個(gè)操作針對(duì)多個(gè)具體節(jié)點(diǎn)角色(可以說有多少個(gè)具體節(jié)點(diǎn)角色就有多少訪問操作),使得所有具體訪問者必須實(shí)現(xiàn)的接口。
(2)、具體訪問者角色(ConcreteVistor):實(shí)現(xiàn)抽象訪問者角色中所有聲明的接口,也可以說是實(shí)現(xiàn)對(duì)每個(gè)具體節(jié)點(diǎn)角色的新的操作。
(3)、抽象節(jié)點(diǎn)角色(Element):聲明一個(gè)接受操作,接受一個(gè)訪問者對(duì)象作為參數(shù),如果有其他參數(shù),可以在這個(gè)“接受操作”里在定義相關(guān)的參數(shù)。
(4)、具體節(jié)點(diǎn)角色(ConcreteElement):實(shí)現(xiàn)抽象元素所規(guī)定的接受操作。
(5)、結(jié)構(gòu)對(duì)象角色(ObjectStructure):節(jié)點(diǎn)的容器,可以包含多個(gè)不同類或接口的容器。
五、訪問者模式的代碼實(shí)現(xiàn)
訪問者這個(gè)模式在我們現(xiàn)實(shí)的編碼生活中使用的并不是很多,我就直接貼代碼,讓大家看代碼的結(jié)構(gòu)吧。今天給大家兩個(gè)代碼實(shí)例,自己慢慢體會(huì)訪問者吧。實(shí)現(xiàn)代碼如下:
static void Main(string[] args) { //如果想執(zhí)行新增加的操作 ShapeVisitor visitor = new CustomVisitor(); AppStructure app = new AppStructure(visitor); Shape shape = new Rectangle(); shape.Draw();//執(zhí)行自己的操作 app.Process(shape);//執(zhí)行新的操作 shape = new Circle(); shape.Draw();//執(zhí)行自己的操作 app.Process(shape);//執(zhí)行新的操作 shape = new Line(); shape.Draw();//執(zhí)行自己的操作 app.Process(shape);//執(zhí)行新的操作 } //抽象圖形定義---相當(dāng)于“抽象節(jié)點(diǎn)角色”Element public abstract class Shape { //畫圖形 public abstract void Draw(); //外界注入具體訪問者 public abstract void Accept(ShapeVisitor visitor); } //抽象訪問者 Visitor public abstract class ShapeVisitor { public abstract void Visit(Rectangle shape); public abstract void Visit(Circle shape); public abstract void Visit(Line shape); //這里有一點(diǎn)要說:Visit方法的參數(shù)可以寫成Shape嗎?就是這樣 Visit(Shape shape),當(dāng)然可以,但是ShapeVisitor子類Visit方法就需要判斷當(dāng)前的Shape是什么類型,是Rectangle類型,是Circle類型,或者是Line類型。 } //具體訪問者 ConcreteVisitor public sealed class CustomVisitor : ShapeVisitor { //針對(duì)Rectangle對(duì)象 public override void Visit(Rectangle shape) { Console.WriteLine("針對(duì)Rectangle新的操作!"); } //針對(duì)Circle對(duì)象 public override void Visit(Circle shape) { Console.WriteLine("針對(duì)Circle新的操作!"); } //針對(duì)Line對(duì)象 public override void Visit(Line shape) { Console.WriteLine("針對(duì)Line新的操作!"); } } //矩形----相當(dāng)于“具體節(jié)點(diǎn)角色” ConcreteElement public sealed class Rectangle : Shape { public override void Draw() { Console.WriteLine("矩形我已經(jīng)畫好!"); } public override void Accept(ShapeVisitor visitor) { visitor.Visit(this); } } //圓形---相當(dāng)于“具體節(jié)點(diǎn)角色”ConcreteElement public sealed class Circle : Shape { public override void Draw() { Console.WriteLine("圓形我已經(jīng)畫好!"); } public override void Accept(ShapeVisitor visitor) { visitor.Visit(this); } } //直線---相當(dāng)于“具體節(jié)點(diǎn)角色” ConcreteElement public sealed class Line : Shape { public override void Draw() { Console.WriteLine("直線我已經(jīng)畫好!"); } public override void Accept(ShapeVisitor visitor) { visitor.Visit(this); } } //結(jié)構(gòu)對(duì)象角色 internal class AppStructure { private ShapeVisitor _visitor; public AppStructure(ShapeVisitor visitor) { this._visitor = visitor; } public void Process(Shape shape) { shape.Accept(_visitor); } }
這是訪問者模式第二種代碼實(shí)例:
static void Main(string[] args) { StoragePlatform platform = new StoragePlatform(); platform.Attach(new Television()); platform.Attach(new Computer()); SizeVisitor sizeVisitor = new SizeVisitor(); StateVisitor stateVisitor = new StateVisitor(); platform.Operate(sizeVisitor); platform.Operate(stateVisitor); } //抽象訪問者角色 Visitor public abstract class Visitor { public abstract void PutTelevision(Television tv); public abstract void PutComputer(Computer comp); } //具體訪問者角色 ConcreteVisitor public sealed class SizeVisitor : Visitor { public override void PutTelevision(Television tv) { Console.WriteLine("按商品大小{0}排放", tv.Size); } public override void PutComputer(Computer comp) { Console.WriteLine("按商品大小{0}排放", comp.Size); } } //具體訪問者角色 ConcreteVisitor public sealed class StateVisitor : Visitor { public override void PutTelevision(Television tv) { Console.WriteLine("按商品新舊值{0}排放", tv.State); } public override void PutComputer(Computer comp) { Console.WriteLine("按商品新舊值{0}排放", comp.State); } } //抽象節(jié)點(diǎn)角色 Element public abstract class Goods { public abstract void Operate(Visitor visitor); private int nSize; public int Size { get { return nSize; } set { nSize = value; } } private int nState; public int State { get { return nState; } set { nState = value; } } } //具體節(jié)點(diǎn)角色 ConcreteElement public sealed class Television : Goods { public override void Operate(Visitor visitor) { visitor.PutTelevision(this); } } //具體節(jié)點(diǎn)角色 ConcreteElement public sealed class Computer : Goods { public override void Operate(Visitor visitor) { visitor.PutComputer(this); } } //結(jié)構(gòu)對(duì)象角色 public sealed class StoragePlatform { private IList<Goods> list = new List<Goods>(); public void Attach(Goods element) { list.Add(element); } public void Detach(Goods element) { list.Remove(element); } public void Operate(Visitor visitor) { foreach (Goods g in list) { g.Operate(visitor); } } }
六、訪問者模式的實(shí)現(xiàn)要點(diǎn):
Visitor模式通過所謂雙重分發(fā)(double dispatch)來實(shí)現(xiàn)在不更改Element類層次結(jié)構(gòu)的前提下,在運(yùn)行時(shí)透明地為類層次結(jié)構(gòu)上的各個(gè)類動(dòng)態(tài)添加新的操作。所謂雙重分發(fā)即Visitor模式中間包括了兩個(gè)多態(tài)分發(fā)(注意其中的多態(tài)機(jī)制):第一個(gè)為accept方法的多態(tài)辨析;第二個(gè)為visit方法的多態(tài)辨析。
設(shè)計(jì)模式其實(shí)是一種堵漏洞的方式,但是沒有一種設(shè)計(jì)模式能夠堵完所有的漏洞,即使是組合各種設(shè)計(jì)模式也是一樣。每個(gè)設(shè)計(jì)模式都有漏洞,都有它們解決不了的情況或者變化。每一種設(shè)計(jì)模式都假定了某種變化,也假定了某種不變化。Visitor模式假定的就是操作變化,而Element類層次結(jié)構(gòu)穩(wěn)定。
(1)、訪問者模式的主要優(yōu)點(diǎn)有:
1】、訪問者模式使得添加新的操作變得容易。如果一些操作依賴于一個(gè)復(fù)雜的結(jié)構(gòu)對(duì)象的話,那么一般而言,添加新的操作會(huì)變得很復(fù)雜。而使用訪問者模式,增加新的操作就意味著添加一個(gè)新的訪問者類。因此,使得添加新的操作變得容易。
2】、訪問者模式使得有關(guān)的行為操作集中到一個(gè)訪問者對(duì)象中,而不是分散到一個(gè)個(gè)的元素類中。這點(diǎn)類似與”中介者模式”。
3】、訪問者模式可以訪問屬于不同的等級(jí)結(jié)構(gòu)的成員對(duì)象,而迭代只能訪問屬于同一個(gè)等級(jí)結(jié)構(gòu)的成員對(duì)象。
(2)、訪問者模式的主要缺點(diǎn)有:
增加新的元素類變得困難。每增加一個(gè)新的元素意味著要在抽象訪問者角色中增加一個(gè)新的抽象操作,并在每一個(gè)具體訪問者類中添加相應(yīng)的具體操作。具體來說,Visitor模式的最大缺點(diǎn)在于擴(kuò)展類層次結(jié)構(gòu)(增添新的Element子類),會(huì)導(dǎo)致Visitor類的改變。因此Visitor模式適用于“Element類層次結(jié)構(gòu)穩(wěn)定,而其中的操作卻經(jīng)常面臨頻繁改動(dòng)”。
(3)、在下面的情況下可以考慮使用訪問者模式:
1】、如果系統(tǒng)有比較穩(wěn)定的數(shù)據(jù)結(jié)構(gòu),而又有易于變化的算法時(shí),此時(shí)可以考慮使用訪問者模式。因?yàn)樵L問者模式使得算法操作的添加比較容易。
2】、如果一組類中,存在著相似的操作,為了避免出現(xiàn)大量重復(fù)的代碼,可以考慮把重復(fù)的操作封裝到訪問者中。(當(dāng)然也可以考慮使用抽象類了)
3】、如果一個(gè)對(duì)象存在著一些與本身對(duì)象不相干,或關(guān)系比較弱的操作時(shí),為了避免操作污染這個(gè)對(duì)象,則可以考慮把這些操作封裝到訪問者對(duì)象中。
七、.NET 訪問者模式的實(shí)現(xiàn)
在現(xiàn)在的Net框架里面,如果要想給現(xiàn)有的類增加新的方法,有了新的方式,那就是“擴(kuò)展方法”,使用起來和實(shí)例方法是一樣一樣的,而且在Net框架里面,微軟自己也寫了很多的擴(kuò)展方法給我們使用。我目前還沒有學(xué)習(xí)到Net的框架類庫(kù)里面有“訪問者模式”實(shí)現(xiàn),看來自己還需努力,革命尚未成功啊。
到此這篇關(guān)于.Net行為型設(shè)計(jì)模式之訪問者模式(Visitor)的文章就介紹到這了。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- .Net行為型設(shè)計(jì)模式之備忘錄模式(Memento)
- .Net行為型設(shè)計(jì)模式之職責(zé)鏈模式(Chain of Responsibility)
- .Net行為型設(shè)計(jì)模式之策略模式(Stragety)
- .Net行為型設(shè)計(jì)模式之狀態(tài)模式(State)
- .Net行為型設(shè)計(jì)模式之中介者模式(Mediator)
- .Net行為型設(shè)計(jì)模式之觀察者模式(Observer)
- .Net行為型設(shè)計(jì)模式之迭代器模式(Iterator)
- .Net行為型設(shè)計(jì)模式之命令模式(Command)
- .Net行為型設(shè)計(jì)模式之模板方法模式(Template?Method)
- .Net行為型設(shè)計(jì)模式之解釋器模式(Interpreter)
相關(guān)文章
.Net行為型設(shè)計(jì)模式之迭代器模式(Iterator)
這篇文章介紹了.Net行為型設(shè)計(jì)模式之迭代器模式(Iterator),文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-05-05Visual Studio快速開發(fā)以及Visual Studio 2010新功能介紹
一直以來都在摸索著vb6.0的一些用法和語(yǔ)法,趁這次培訓(xùn)的機(jī)會(huì)正好整理Visual Studio的一些快速開發(fā)的技巧,還有一些vs2010的新功能收集和體會(huì),把培訓(xùn)的一些文檔性質(zhì)的記錄下來,希望對(duì)各位有用2011-12-12使用vs2022在.net6中調(diào)試帶typescript的靜態(tài)頁(yè)面
這篇文章介紹了使用vs2022在.net6中調(diào)試帶typescript的靜態(tài)頁(yè)面,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-12-12ASP.NET MVC把數(shù)據(jù)庫(kù)中枚舉項(xiàng)的數(shù)字轉(zhuǎn)換成文字
這篇文章介紹了ASP.NET MVC把數(shù)據(jù)庫(kù)中枚舉項(xiàng)的數(shù)字轉(zhuǎn)換成文字的方法,文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-10-10.Net行為型設(shè)計(jì)模式之觀察者模式(Observer)
這篇文章介紹了.Net行為型設(shè)計(jì)模式之觀察者模式(Observer),文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-05-05