.Net行为型设计模式之访问者模式(Visitor)
作者:springsnow 发布时间:2024-05-13 09:18:14
一、动机(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
猜你喜欢
- 1.Order By原理MySQL的Order By操作用于排序,并且会有多种不同的排序算法,他们的性能都是不一样的。假设有一个表,建表的s
- 在scipy.linalg的函数中,往往会提供两种参数,其一是check_finite,当为True时将进行有限检查,另一类是overwri
- 除了传递给日志记录函数的参数(如msg)外,有时候我们还想在日志输出中包含一些额外的上下文信息。比如,在一个网络应用中,可能希望在日志中记录
- 在SQL Server数据库中,有min server memory与max server memory两个内存选项。数据库管理员合理设置这
- 今天笔者带大家,梳理几个常见的基于文本终端的 UI 框架,一睹为快!Curses首先出场的是 Curses。Curses 是一个能提供基于文
- Plotly Express是对 Plotly.py 的高级封装,内置了大量实用、现代的绘图模板,用户只需调用简单的API函数,即可快速生成
- 前言不知道大伙有没有看到过这一句话:“中国(疫苗研发)非常困难,因为在中国我们没有办法做第三期临床试验,因为没有病人了。
- 前言:大家好,今天和大家分享自己总结的6个常用的 Python 数据处理代码,对于经常处理数据的coder最好熟练掌握。1、选取有空值的行在
- 打包依赖文件至目标程序目录问题我使用 PySide2 开发了一个应用程序,为了方便其他人使用,我便使用 PyInstaller 打包成 *.
- 本文实例分析了JS获取年月日时分秒的方法。分享给大家供大家参考,具体如下:var d = new Date();var time = d.g
- Python中格式化format()方法详解Python中格式化输出字符串使用format()函数, 字符串即类, 可以使用方法
- 前言这里要说明一下,本文包含的代码其中一部分并不是自己写的,是我找了很多文章拼凑出来的,比如如何找相同内容的单元格、怎么合并、怎么居中等等。
- 问题引入作为一名Golang开发者,线上环境遇到过好几次连接数暴增问题(mysql/redis/kafka等)。纠其原因,Golang作为常
- 使用stitcher需要注意,图像太大会报错而且计算慢。特点和适用范围:图像需有足够重合相同特征区域。优点:适应部分倾斜/尺度变换和畸变情形
- 最近更新了VS Code之后,发现Remote-ssh拓展里的端口转发功能没了,很伤心,在探索的同时,顺手配置了一下VS Code ssh免
- 先看map。map()函数接收两个参数,一个是函数,一个是序列,map将传入的函数依次作用到序列的每个元素,并把结果作为新的list返回。举
- 1. xlsx to csv:import pandas as pddef xlsx_to_csv_pd(): data_xls = pd.
- 代码:function checkall(checkNames){ var allBoxs = document.getElem
- 以前看到 andy的关于“Quiet Structure”觉的很不错,于是今天到她的个人站点上逛逛,发现不少好的文章,今天介绍的是
- 今天下载wordcloud的时候出现了很多问题,在此总结总结1.问题一:You are using pip version 19.0.3,