網站首頁 編程語言 正文
一、動機(Motivate)
在軟件構建過程中,由于需求的改變,某些類層次結構中常常需要增加新的行為(方法),如果直接在基類中做這樣的更改,將會給子類帶來很繁重的變更負擔,甚至破壞原有設計。如何在不更改類層次結構的前提下,在運行時根據需要透明地為類層次結構上的各個類動態添加新的操作,從而避免上述問題?
二、意圖(Intent)
表示一個作用于某對象結構中的各個元素的操作。它可以在不改變各元素的類的前提下定義作用于這些元素的新的操作。???????????????????????????????? ——《設計模式》GoF
三、結構圖(Structure)
四、模式的組成
可以看出,在訪問者模式的結構圖有以下角色:
(1)、抽象訪問者角色(Vistor): 聲明一個包括多個訪問操作,多個操作針對多個具體節點角色(可以說有多少個具體節點角色就有多少訪問操作),使得所有具體訪問者必須實現的接口。
(2)、具體訪問者角色(ConcreteVistor):實現抽象訪問者角色中所有聲明的接口,也可以說是實現對每個具體節點角色的新的操作。
(3)、抽象節點角色(Element):聲明一個接受操作,接受一個訪問者對象作為參數,如果有其他參數,可以在這個“接受操作”里在定義相關的參數。
(4)、具體節點角色(ConcreteElement):實現抽象元素所規定的接受操作。
(5)、結構對象角色(ObjectStructure):節點的容器,可以包含多個不同類或接口的容器。
五、訪問者模式的代碼實現
訪問者這個模式在我們現實的編碼生活中使用的并不是很多,我就直接貼代碼,讓大家看代碼的結構吧。今天給大家兩個代碼實例,自己慢慢體會訪問者吧。實現代碼如下:
static void Main(string[] args)
{
//如果想執行新增加的操作
ShapeVisitor visitor = new CustomVisitor();
AppStructure app = new AppStructure(visitor);
Shape shape = new Rectangle();
shape.Draw();//執行自己的操作
app.Process(shape);//執行新的操作
shape = new Circle();
shape.Draw();//執行自己的操作
app.Process(shape);//執行新的操作
shape = new Line();
shape.Draw();//執行自己的操作
app.Process(shape);//執行新的操作
}
//抽象圖形定義---相當于“抽象節點角色”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);
//這里有一點要說:Visit方法的參數可以寫成Shape嗎?就是這樣 Visit(Shape shape),當然可以,但是ShapeVisitor子類Visit方法就需要判斷當前的Shape是什么類型,是Rectangle類型,是Circle類型,或者是Line類型。
}
//具體訪問者 ConcreteVisitor
public sealed class CustomVisitor : ShapeVisitor
{
//針對Rectangle對象
public override void Visit(Rectangle shape)
{
Console.WriteLine("針對Rectangle新的操作!");
}
//針對Circle對象
public override void Visit(Circle shape)
{
Console.WriteLine("針對Circle新的操作!");
}
//針對Line對象
public override void Visit(Line shape)
{
Console.WriteLine("針對Line新的操作!");
}
}
//矩形----相當于“具體節點角色” ConcreteElement
public sealed class Rectangle : Shape
{
public override void Draw()
{
Console.WriteLine("矩形我已經畫好!");
}
public override void Accept(ShapeVisitor visitor)
{
visitor.Visit(this);
}
}
//圓形---相當于“具體節點角色”ConcreteElement
public sealed class Circle : Shape
{
public override void Draw()
{
Console.WriteLine("圓形我已經畫好!");
}
public override void Accept(ShapeVisitor visitor)
{
visitor.Visit(this);
}
}
//直線---相當于“具體節點角色” ConcreteElement
public sealed class Line : Shape
{
public override void Draw()
{
Console.WriteLine("直線我已經畫好!");
}
public override void Accept(ShapeVisitor visitor)
{
visitor.Visit(this);
}
}
//結構對象角色
internal class AppStructure
{
private ShapeVisitor _visitor;
public AppStructure(ShapeVisitor visitor)
{
this._visitor = visitor;
}
public void Process(Shape shape)
{
shape.Accept(_visitor);
}
}
這是訪問者模式第二種代碼實例:
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);
}
}
//抽象節點角色 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; }
}
}
//具體節點角色 ConcreteElement
public sealed class Television : Goods
{
public override void Operate(Visitor visitor)
{
visitor.PutTelevision(this);
}
}
//具體節點角色 ConcreteElement
public sealed class Computer : Goods
{
public override void Operate(Visitor visitor)
{
visitor.PutComputer(this);
}
}
//結構對象角色
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);
}
}
}
六、訪問者模式的實現要點:
Visitor模式通過所謂雙重分發(double dispatch)來實現在不更改Element類層次結構的前提下,在運行時透明地為類層次結構上的各個類動態添加新的操作。所謂雙重分發即Visitor模式中間包括了兩個多態分發(注意其中的多態機制):第一個為accept方法的多態辨析;第二個為visit方法的多態辨析。
設計模式其實是一種堵漏洞的方式,但是沒有一種設計模式能夠堵完所有的漏洞,即使是組合各種設計模式也是一樣。每個設計模式都有漏洞,都有它們解決不了的情況或者變化。每一種設計模式都假定了某種變化,也假定了某種不變化。Visitor模式假定的就是操作變化,而Element類層次結構穩定。
(1)、訪問者模式的主要優點有:
1】、訪問者模式使得添加新的操作變得容易。如果一些操作依賴于一個復雜的結構對象的話,那么一般而言,添加新的操作會變得很復雜。而使用訪問者模式,增加新的操作就意味著添加一個新的訪問者類。因此,使得添加新的操作變得容易。
2】、訪問者模式使得有關的行為操作集中到一個訪問者對象中,而不是分散到一個個的元素類中。這點類似與”中介者模式”。
3】、訪問者模式可以訪問屬于不同的等級結構的成員對象,而迭代只能訪問屬于同一個等級結構的成員對象。
(2)、訪問者模式的主要缺點有:
增加新的元素類變得困難。每增加一個新的元素意味著要在抽象訪問者角色中增加一個新的抽象操作,并在每一個具體訪問者類中添加相應的具體操作。具體來說,Visitor模式的最大缺點在于擴展類層次結構(增添新的Element子類),會導致Visitor類的改變。因此Visitor模式適用于“Element類層次結構穩定,而其中的操作卻經常面臨頻繁改動”。
(3)、在下面的情況下可以考慮使用訪問者模式:
1】、如果系統有比較穩定的數據結構,而又有易于變化的算法時,此時可以考慮使用訪問者模式。因為訪問者模式使得算法操作的添加比較容易。
2】、如果一組類中,存在著相似的操作,為了避免出現大量重復的代碼,可以考慮把重復的操作封裝到訪問者中。(當然也可以考慮使用抽象類了)
3】、如果一個對象存在著一些與本身對象不相干,或關系比較弱的操作時,為了避免操作污染這個對象,則可以考慮把這些操作封裝到訪問者對象中。
七、.NET 訪問者模式的實現
在現在的Net框架里面,如果要想給現有的類增加新的方法,有了新的方式,那就是“擴展方法”,使用起來和實例方法是一樣一樣的,而且在Net框架里面,微軟自己也寫了很多的擴展方法給我們使用。我目前還沒有學習到Net的框架類庫里面有“訪問者模式”實現,看來自己還需努力,革命尚未成功啊。
原文鏈接:https://www.cnblogs.com/springsnow/p/11362008.html
相關推薦
- 2022-07-20 Python3.7.2環境安裝
- 2022-07-07 redis連接報錯error:NOAUTH?Authentication?required_Redi
- 2022-02-22 Android對話框AlertDialog詳解_Android
- 2022-09-06 C語言常見排序算法之交換排序(冒泡排序,快速排序)_C 語言
- 2022-04-26 Entity?Framework?Core延遲加載(懶加載)用法_實用技巧
- 2022-07-25 python數據清洗中的時間格式化實現_python
- 2022-08-07 C#實現關機功能_C#教程
- 2022-04-16 ASP.NET?Core命令行界面CLI用法_基礎應用
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支