本笔记摘抄自:https://www.cnblogs.com/PatrickLiu/p/8135083.html,记录一下学习过程以备后续查用。

一、引言

今天我们要讲行为型设计模式的第九个模式--访问者模式。如果按老规矩,先从名称上来看这个模式,我根本不能获得任何对理解该模式有用的信息,

而且这个模式在我们的编码生活中使用的并不是很多。该模式的意图定义很抽象,第一次看了这个定义其实和没看没有什么区别,一头雾水。为了让大家

更好的理解该模式的初衷,我们举个例子来说明模式。比如:当客户提出一个新的软件需求的时候,大家经过多个日以继夜的努力,终于通过一个比较不

错的软件设计解决了客户的需求,而且这个设计有相对完美的类层次结构并且符合OO的设计原则,大家为此感到开心,颇有成就感。又过了一段时间,

客户突然又有一个新的需求,需要在现有的类层次结构里面增加一个新的操作(其实就是一个方法),怎么办?还好,在面向OO设计模式中有一个模式

就是为了解决这个问题的,那就是访问者模式,下面让我们好好地了解一下该模式。

    二、访问者模式介绍

访问者模式:英文名称--Visitor Pattern;分类--行为型。

    2.1、动机(Motivate)

在软件构建过程中,由于需求的改变,某些类层次结构中常常需要增加新的行为(方法),如果直接在基类中做这样的更改,将会给子类带来很繁重的

变更负担,甚至破坏原有设计。如何在不更改类层次结构的前提下,在运行时根据需要透明地为类层次结构上的各个类动态添加新的操作?

    2.2、意图(Intent)

表示一个作用于某对象结构中的各个元素的操作。它可以在不改变各元素的类的前提下定义作用于这些元素的新的操作。——《设计模式》GoF

    2.3、结构图(Structure)

    2.4、模式的组成

可以看出,在访问者模式的结构图有以下角色:

1)抽象访问者角色(Visitor): 声明一个包括多个访问操作,多个操作针对多个具体节点角色(可以说有多少个具体节点角色就有多少访问操作),使

得所有具体访问者必须实现的接口。

2)具体访问者角色(ConcreteVisitor):实现抽象访问者角色中所有声明的接口,也可以说是实现对每个具体节点角色的新的操作。

3)抽象节点角色(Element):声明一个接受操作,接受一个访问者对象作为参数,如果有其它参数,可以在这个“接受操作”里在定义相关的参数。

4)具体节点角色(ConcreteElement):实现抽象元素所规定的接受操作。

5)结构对象角色(ObjectStructure):节点的容器,可以包含多个不同类或接口的容器。

    2.5、访问者模式的具体实现

访问者模式在我们现实的编码生活中使用的并不是很多,我就直接贴代码,让大家看看代码的结构吧。今天给大家两个代码实例,实现代码如下:

    class Program
{
/// <summary>
/// 抽象图形定义--相当于“抽象节点角色”
/// </summary>
public abstract class Shape
{
//画图形
public abstract void Draw();
//外界注入具体访问者
public abstract void Accept(ShapeVisitor visitor);
} /// <summary>
/// 矩形--相当于“具体节点角色”
/// </summary>
public sealed class Rectangle : Shape
{
public override void Draw()
{
Console.WriteLine("矩形我已经画好了。");
} public override void Accept(ShapeVisitor visitor)
{
visitor.Visit(this);
}
} /// <summary>
/// 圆形--相当于“具体节点角色”
/// </summary>
public sealed class Circle : Shape
{
public override void Draw()
{
Console.WriteLine("圆形我已经画好了。");
} public override void Accept(ShapeVisitor visitor)
{
visitor.Visit(this);
}
} /// <summary>
/// 直线--相当于“具体节点角色”
/// </summary>
public sealed class Line : Shape
{
public override void Draw()
{
Console.WriteLine("直线我已经画好了。");
} public override void Accept(ShapeVisitor visitor)
{
visitor.Visit(this);
}
} /// <summary>
/// 抽象访问者
/// </summary>
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类型?
} /// <summary>
/// 具体访问者
/// </summary>
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新的操作。");
}
} /// <summary>
/// 结构对象角色
/// </summary>
internal class AppStructure
{
private readonly ShapeVisitor _visitor; public AppStructure(ShapeVisitor visitor)
{
_visitor = visitor;
} public void Process(Shape shape)
{
shape.Accept(_visitor);
}
} static void Main(string[] args)
{
#region 访问者模式(第一个实例)
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); //执行新的操作 Console.ReadLine();
#endregion
}
}

运行结果如下:

这是访问者模式第二个代码实例:

    class Program
{
/// <summary>
/// 抽象访问者角色
/// </summary>
public abstract class Visitor
{
public abstract void PutTelevision(Television tv); public abstract void PutComputer(Computer comp);
} /// <summary>
/// 具体访问者角色
/// </summary>
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);
}
} /// <summary>
/// 具体访问者角色
/// </summary>
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);
}
} /// <summary>
/// 抽象节点角色
/// </summary>
public abstract class Goods
{
public abstract void Operate(Visitor visitor);
public int Size { get; set; }
public int State { get; set; }
} /// <summary>
/// 具体节点角色
/// </summary>
public sealed class Television : Goods
{
public override void Operate(Visitor visitor)
{
visitor.PutTelevision(this);
}
} /// <summary>
/// 具体节点角色
/// </summary>
public sealed class Computer : Goods
{
public override void Operate(Visitor visitor)
{
visitor.PutComputer(this);
}
} /// <summary>
/// 结构对象角色
/// </summary>
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);
}
}
} static void Main(string[] args)
{
#region 访问者模式(第二个实例)
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); Console.Read();
#endregion
}
}

运行结果如下:

三、访问者模式的实现要点

Visitor模式通过所谓双重分发(double dispatch)来实现在不更改Element类层次结构的前提下,在运行时透明地为类层次结构上的各个类动态添加新

的操作。所谓双重分发即Visitor模式中间包括了两个多态分发(注意其中的多态机制):第一个为accept方法的多态辨析;第二个为visit方法的多态辨析。

设计模式其实是一种堵漏洞的方式,但是没有一种设计模式能够堵完所有的漏洞,即使是组合各种设计模式也是一样。每个设计模式都有漏洞,都有

它们解决不了的情况或者变化。每一种设计模式都假定了某种变化,也假定了某种不变化。Visitor模式假定的就是操作变化,而Element类层次结构稳定。

3.1、访问者模式的主要优点

1)访问者模式使得添加新的操作变得容易。如果一些操作依赖于一个复杂的结构对象的话,那么一般而言,添加新的操作会变得很复杂。而使用访问

者模式,增加新的操作就意味着添加一个新的访问者类。因此,使得添加新的操作变得容易。

2)访问者模式使得有关的行为操作集中到一个访问者对象中,而不是分散到一个个的元素类中。这点类似与”中介者模式”。

3)访问者模式可以访问属于不同的等级结构的成员对象,而迭代只能访问属于同一个等级结构的成员对象。

3.2、访问者模式的主要缺点

1)增加新的元素类变得困难。每增加一个新的元素意味着要在抽象访问者角色中增加一个新的抽象操作,并在每一个具体访问者类中添加相应的具体

操作。具体来说,Visitor模式的最大缺点在于扩展类层次结构(增添新的Element子类),会导致Visitor类的改变。因此Visitor模式适用于“Element类层次

结构稳定,而其中的操作却经常面临频繁改动”。

3.3、在下面的情况下可以考虑使用访问者模式

1)如果系统有比较稳定的数据结构,而又有易于变化的算法时,此时可以考虑使用访问者模式,因为访问者模式使得算法操作的添加比较容易。

2)如果一组类中,存在着相似的操作,为了避免出现大量重复的代码,可以考虑把重复的操作封装到访问者中(当然也可以考虑使用抽象类了)。

3)如果一个对象存在着一些与本身对象不相干或关系比较弱的操作时,为了避免操作污染这个对象,则可以考虑把这些操作封装到访问者对象中。

四、.NET中访问者模式的实现

在现在的.Net框架里面,如果要想给现有的类增加新的方法,有了新的方式,那就是“扩展方法”,使用起来和实例方法是一样一样的,而且在.Net

框架里面,微软自己也写了很多的扩展方法给我们使用。我目前还没有学习到.Net的框架类库里面有“访问者模式”实现,看来自己还需努力,革命尚

未成功啊。

五、总结

访问者模式写完了,这个模式刚开始理解起来还是挺麻烦的,但是,如果我们多看几个实例代码,完全掌握也不是问题。随着C#语言的发展,设

计模式里面的很多东西,我们可以通过C#语言的一些特性做更好的替代。我们写设计模式刚开始要慢慢来,一步一步照猫画虎地来写代码,等我们

熟练掌握了模式的核心思想,我们就要写符合C#风格和特性的模式代码了,或者说我们要用C#来写设计模式了,写出来的代码也会更棒。

C#设计模式学习笔记:(21)访问者模式的更多相关文章

  1. 设计模式学习笔记——Visitor 访问者模式

    1.定义IVisitor接口,确定变化所涉及的方法 2.封装变化类.实现IVisitor接口 3.在实体类的变化方法中传入IVisitor接口,由接口确定使用哪一种变化来实现(封装变化) 4.在使用时 ...

  2. 设计模式学习笔记--备忘录(Mamento)模式

    写在模式学习之前 什么是设计模式:在我们进行程序设计时,逐渐形成了一些典型问题和问题的解决方式,这就是软件模式:每个模式描写叙述了一个在我们程序设计中常常发生的问题,以及该问题的解决方式:当我们碰到模 ...

  3. 设计模式学习笔记-Adapter模式

    Adapter模式,就是适配器模式,使两个原本没有关联的类结合一起使用. 平时我们会经常碰到这样的情况,有了两个现成的类,它们之间没有什么联系,但是我们现在既想用其中一个类的方法,同时也想用另外一个类 ...

  4. Java-马士兵设计模式学习笔记-装饰者模式

    Java装饰者模式简介 一.假设有一个Worker接口,它有一个doSomething方法,Plumber和Carpenter都实现了Worker接口,代码及关系如下: 1.Worker.java p ...

  5. 研磨设计模式学习笔记2--外观模式Facade

    需求:客户端需要按照需求,执行一个操作,操作包括一个系统中的3个模块(根据配置选择是否全部执行). 外观模式优点: 客户端无需知道系统内部实现,,只需要写好配置文件,控制那些模块执行,简单易用. 外观 ...

  6. 设计模式学习笔记 1.factory 模式

    Factory 模式 用户不关心工厂的具体类型,只知道这是一个工厂就行. 通过工厂的实现推迟到子类里面去来确定工厂的具体类型. 工厂的具体类型来确定生产的具体产品. 同时用户不关心这是一个什么样子的产 ...

  7. 设计模式学习笔记——Composite 组合模式

    用于描述无限层级的复杂对象,类似于描述资源管理器,抽象出每一个层级的共同特点(文件夹和文件,展开事件) 以前描述一个对象,是将整个对象的全部数据都描述清楚,而组合模式通过在对象中定义自己,描述自己的下 ...

  8. 设计模式学习笔记——Bridge 桥接模式

    先说一下我以前对桥接模式的理解:当每个类中都使用到了同样的属性或方法时,应该将他们单独抽象出来,变成这些类的属性和方法(避免重复造轮子),当时的感觉是和三层模型中的model有点单相似,也就是让mod ...

  9. Java-马士兵设计模式学习笔记-责任链模式-FilterChain功能

    一.目标 增加filterchain功能 二.代码 1.Filter.java public interface Filter { public String doFilter(String str) ...

  10. Java-马士兵设计模式学习笔记-责任链模式-处理数据

    一.目标 数据提交前做各种处理 二.代码 1.MsgProcessor.java public class MsgProcessor { private List<Filter> filt ...

随机推荐

  1. OpenCV图像数字化

    灰度图像数字化 我们平时使用PS或者其它图像处理的软件打开一个要处理的图像,当我们将图像放大的足够大的时候我们会发现很多个灰度程度不同的小方格,其中每个方格就相当于一个像素,水平方向的方格数代表这个图 ...

  2. latex一些有用的写法

    编辑博文的时候总是忘语法,然后到网上查-- 干脆记一下! 1.编辑漂亮的函数上下标 \(\sum\limits_{i=1}^n\) 对于原有的函数:$\sum\limits_{i=1}^n$ \(\m ...

  3. 「 深入浅出 」集合Set

    系列文章 「 深入浅出 」集合List 「 深入浅出 」java集合Collection和Map Set继承自Collection接口,不能包含有重复元素.本篇文章主要讲Set中三个比较重要的实现类: ...

  4. 玩转Django2.0---Django笔记建站基础十二(Django项目上线部署)

    第十二章 Django项目上线部署 目前部署Django项目有两种主流方案:Nginx+uWsGI+Django或者Apache+uWSGI+Django.Nginx作为服务器最前端,负责接收浏览器的 ...

  5. linux ftp 服务器 ,web 服务器 配置

    ftp服务器配置: 1:vsftpd.conf 2;vsftpd.ftpusers 3:vsftpd.user_list web服务器配置: apache 配置

  6. [集训]dance

    题意 http://uoj.ac/problem/77 思考 显然能转化为最小割模型.若没有pi的代价,则对于第i个格子,可以让源点连向第i个点,容量为黑色收益,再连向汇点,容量为白色收益.再考虑pi ...

  7. 牛客网在线编程_有序矩阵中第K小的元素

    Leetcode378原题,所以一样没有数据范围...( log(max-min)二分答案,然后NlogN二分每一行求出小于答案的元素个数,为了保证二分的答案在矩阵中,二分写的要和平常不太一样,最后输 ...

  8. Docker三剑客之swarm

    简介 swarm是一种docker集群管理工具,跟三剑客前两者不同的是:compose是一种统一编排的工具,machine是一种远程控制工具,swarm则是将多个docker主机映射成一个docker ...

  9. Codeforces_327_C

    http://codeforces.com/problemset/problem/327/C 等比求和相加,有mod的出现,所以要算逆元. #include<iostream> #incl ...

  10. WeChall_Training: Crypto - Caesar I (Crypto, Training)

    As on most challenge sites, there are some beginner cryptos, and often you get started with the good ...