一、引言

今天我们开始讲“行为型”设计模式的第九个模式,该模式是【访问者模式】,英文名称是:Visitor Pattern。如果按老规矩,先从名称上来看看这个模式,我根本不能获得任何对理解该模式有用的信息,而且这个模式在我们的编码生活中使用的并不是很多。该模式的意图定义很抽象,第一次看了这个定义其实和没看没有什么区别,一头雾水,为了让大家更好的理解该模式的初衷,我们举个例子来说明模式。比如:当我们为了解决一个新的软件需求的时候,经过多个日以继夜的努力,最终通过一个完美(自己认为的)的软件设计解决了客户提出的新的需求,而且这个设计有完美的类层次结构,并且是符合OO的设计原则的,我们很开心,对自己设计的东西很有成就感。又过了一段时间,客户突然又有了一个新的需求,需要为现有的类层次结构里面的类增加一个新的操作(其实就是一个方法),怎么办?好办,在面向OO设计模式中有一个模式就是为了解决这个问题的,那就是“访问者模式”,可以为现有的类层次结构中的类轻松增加新的操作,我们继续吧,好好的了解一下该模式。

二、访问者模式的详细介绍

2.1、动机(Motivate)

在软件构建过程中,由于需求的改变,某些类层次结构中常常需要增加新的行为(方法),如果直接在基类中做这样的更改,将会给子类带来很繁重的变更负担,甚至破坏原有设计。如何在不更改类层次结构的前提下,在运行时根据需要透明地为类层次结构上的各个类动态添加新的操作,从而避免上述问题?

2.2、意图(Intent)

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

2.3、结构图(Structure)

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

(1)、抽象访问者角色(Vistor): 声明一个包括多个访问操作,多个操作针对多个具体节点角色(可以说有多少个具体节点角色就有多少访问操作),使得所有具体访问者必须实现的接口。

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

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

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

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

2.5、访问者模式的代码实现

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

 namespace Vistor
{
//抽象图形定义---相当于“抽象节点角色”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);
}
} class Program
{
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);//执行新的操作 Console.ReadLine();
}
}
}

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

 namespace Visitor
{
//抽象访问者角色 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);
}
}
} class Program
{
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); Console.Read();
}
}
}

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

Visitor模式通过所谓双重分发(double dispatch)来实现在不更改Element类层次结构的前提下,在运行时透明地为类层次结构上的各个类动态添加新的操作。所谓双重分发即Visitor模式中间包括了两个多态分发(注意其中的多态机制):第一个为accept方法的多态辨析;第二个为visit方法的多态辨析。

  设计模式其实是一种堵漏洞的方式,但是没有一种设计模式能够堵完所有的漏洞,即使是组合各种设计模式也是一样。每个设计模式都有漏洞,都有它们解决不了的情况或者变化。每一种设计模式都假定了某种变化,也假定了某种不变化。Visitor模式假定的就是操作变化,而Element类层次结构稳定。

(1)、访问者模式的主要优点有:

1】、访问者模式使得添加新的操作变得容易。如果一些操作依赖于一个复杂的结构对象的话,那么一般而言,添加新的操作会变得很复杂。而使用访问者模式,增加新的操作就意味着添加一个新的访问者类。因此,使得添加新的操作变得容易。

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

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

 (2)、访问者模式的主要缺点有:

1】、增加新的元素类变得困难。每增加一个新的元素意味着要在抽象访问者角色中增加一个新的抽象操作,并在每一个具体访问者类中添加相应的具体操作。具体来说,Visitor模式的最大缺点在于扩展类层次结构(增添新的Element子类),会导致Visitor类的改变。因此Visitor模式适用于“Element类层次结构稳定,而其中的操作却经常面临频繁改动”。

(3)、在下面的情况下可以考虑使用访问者模式:

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

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

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

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

在现在的Net框架里面,如果要想给现有的类增加新的方法,有了新的方式,那就是“扩展方法”,使用起来和实例方法是一样一样的,而且在Net框架里面,微软自己也写了很多的扩展方法给我们使用。我目前还没有学习到Net的框架类库里面有“访问者模式”实现,看来自己还需努力,革命尚未成功啊。

五、总结

访问者模式写完了,这个模式刚开始理解起来还是挺麻烦的,但是,如果我们多看几个实例代码,完全掌握也不是问题。随着C#语言的发展,设计模式里面的很多东西,我们可以通过C#语言的一些特性做更好的替代。我们写设计模式刚开始要慢慢来,一步一步的照猫画虎的来写代码,等我们熟练掌握了模式的核心意思,我们就要写符合C#风格和特性的模式代码了,或者说我们要用C#来写设计模式了,写出来的代码会更棒。

C#设计模式之二十二访问者模式(Visitor Pattern)【行为型】的更多相关文章

  1. 二十四种设计模式:访问者模式(Visitor Pattern)

    访问者模式(Visitor Pattern) 介绍表示一个作用于某对象结构中的各元素的操作.它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作. 示例有一个Message实体类,某些对象对 ...

  2. 乐在其中设计模式(C#) - 访问者模式(Visitor Pattern)

    原文:乐在其中设计模式(C#) - 访问者模式(Visitor Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 访问者模式(Visitor Pattern) 作者:webabc ...

  3. 访问者模式(Visitor Pattern)——操作复杂对象结构

    模式概述 在软件开发中,可能会遇到操作复杂对象结构的场景,在该对象结构中存储了多个不同类型的对象信息,而且对同一对象结构中的元素的操作方式并不唯一,可能需要提供多种不同的处理方式,还有可能增加新的处理 ...

  4. C#设计模式——访问者模式(Visitor Pattern)

    一.概述由于需求的改变,某些类常常需要增加新的功能,但由于种种原因这些类层次必须保持稳定,不允许开发人员随意修改.对此,访问者模式可以在不更改类层次结构的前提下透明的为各个类动态添加新的功能.二.访问 ...

  5. [设计模式] 23 访问者模式 visitor Pattern

    在GOF的<设计模式:可复用面向对象软件的基础>一书中对访问者模式是这样说的:表示一个作用于某对象结构中的各元素的操作.它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作.访问 ...

  6. 十一个行为模式之访问者模式(Visitor Pattern)

    定义: 提供一个作用于某对象结构(通常是一个对象集合)的操作的接口,使得在添加新的操作或者在添加新的元素时,不需要修改原有系统,就可以对各个对象进行操作. 结构图: Visitor:抽象访问者类,对元 ...

  7. 访问者模式-Visitor Pattern

    1.主要优点 访问者模式的主要优点如下: (1) 增加新的访问操作很方便.使用访问者模式,增加新的访问操作就意味着增加一个新的具体访问者类,实现简单,无须修改源代码,符合“开闭原则”. (2) 将有关 ...

  8. 设计模式 ( 二十 ) 访问者模式Visitor(对象行为型)

    设计模式 ( 二十 ) 访问者模式Visitor(对象行为型) 1.概述 在软件开发过程中,对于系统中的某些对象,它们存储在同一个集合collection中,且具有不同的类型,而且对于该集合中的对象, ...

  9. 访问者模式 Visitor 行为型 设计模式(二十七)

    访问者模式 Visitor    <侠客行>是当代作家金庸创作的长篇武侠小说,新版电视剧<侠客行>中,开篇有一段独白:  “茫茫海外,传说有座侠客岛,岛上赏善罚恶二使,每隔十年 ...

  10. 备忘录模式 Memento 快照模式 标记Token模式 行为型 设计模式(二十二)

    备忘录模式 Memento   沿着脚印,走过你来时的路,回到原点.     苦海翻起爱恨   在世间难逃避命运   相亲竟不可接近   或我应该相信是缘份   一首<一生所爱>触动了多少 ...

随机推荐

  1. 史上最强学生管理系统之ArrayList版

    其实不管是网上或者培训班,都会有学生管理系统的最基础版本,本人也不过是照猫画虎,在某些细节方面进行了一些渲染,使这个最基本的小程序更加人性化和便于利于操作一点,个人愚见,大牛勿喷,欢迎转载(请注明出处 ...

  2. ORA-01745: 无效的主机/绑定变量名 ORA-00917: 缺失的逗号 oracle日期格式错误

    今天在oracle中执行插入语句的时候报了一个奇怪的错误,在程序中报的错误是ORA-01745: 无效的主机/绑定变量名,网上一查说是缺失逗号,在查询分析器执行的时候报缺失的逗号,仔细看了一下也没有缺 ...

  3. 4)C语言指针(C自考学习)

    指针和指针变量 指针就是地址,地址是一种数据类型.指针变量也是变量,但只能存放地址类型的数据,可以称为"地址型"变量. 1)内存单元和地址 一个程序运行时,程序本身和程序中用到的数 ...

  4. 重温CSS3

    基础不牢,地动山摇!没办法,只能重温"经典"! 1.CSS3边框:border-radius; box-shadow; border-image border-radius:r1, ...

  5. Natural Merge Sort(自然归并排序)

    This is a Natural Merge Sort program from my textbook. It works, but I don't think it's good. // Nat ...

  6. Spring4 IOC详解

    Spring4 IOC详解 上一章对Spring做一个快速入门的教程,其中只是简单的提到了IOC的特性.本章便对Spring的IOC进行一个详解.主要从三个方面开始:基于xml文件的Bean配置,基于 ...

  7. struts2对于处理JSON的配置

    由于最近几年日益流行前后端分离模式,JSON作为数据载体也变得不可或缺.几乎所有的web框架都需要支持JSON,下面咱就一起了解下struts2是如何支持JSON的. 对于JSON的发送 这里有两种方 ...

  8. String的Intern方法

    jdk6 和 jdk7 下 intern 的区别 相信很多 JAVA 程序员都做做类似 String s = new String("abc")这个语句创建了几个对象的题目. 这种 ...

  9. [flask/python/web] 解析flask web开发(Miguel著)一书第11章主页不显示博文表单的问题

    ---------------------------------------------以下内容2017.7.14更新---------------------------------------- ...

  10. LVS+keepalived快速搭建测试环境

    #LVS+keepalived快速搭建测试环境 #LVS+keepalived快速搭建测试环境 #centos6 X64 # LVS 负载均衡模式:DR(直接路由) 192.168.18.31 mas ...