访问者模式

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

这个模式相对比较复杂, 而又很少能被用上, 拿GOF作者的话'大多数时候你并不需要访问者模式, 但当你一旦需要它, 那就是真正的需要'

访问者模式的本质就是解数据结构和结构上的操作之间的耦合, 使操作集合可以自由的演化.
看上去很美好, 不过有个比较苛刻的条件, 就是数据结构的类层次是稳定的, 不变的, 才可以使用.
如下图, Element的子类只有ConcreteElement1和ConcreteElement2, 不会变化, 举个比较典型的例子, 人的性别, 只有男,女, 不会变的很稳定, 这样就可以使用访问者模式. 所以能满足这种条件的很少, 于是这个模式很少被用到.

场景, 为数据结构添加新的操作newfunc, 即Element添加新的成员函数newfunc()
正规的做法就是, 修改Element, ConcreteElement1, ConcreteElement2来添加newfunc. 这样做违反开闭原则, 而且当这个数据结构的操作变化频繁的时候, 就很麻烦了.

这个模式的做法, 既然操作频繁变化, 就把操作独立出来, 对于每个操作生成一个Visitor子类. 对于一个操作而言, 对ConcreteElement1, ConcreteElement2的具体实现是不同的, 所以在ConcreteVisitor1类中, 要分别为ConcreteElement1, ConcreteElement2定义不同的操作函数.
这儿就解释了为什么, Element的类层次必须稳定, 如果这儿增加一个ConcreteElement3, 那么在所有的Visitor中都必须添加相应的处理函数, 这就回到了上面的问题. 所以只有Element的类层次不变, 我们这样做才又意义.
这个模式的好处就是, 在增加操作时, 不需要改变数据结构类, 只需要定义一个新的操作子类即可, 符合开闭原则.

public abstract class Visitor //把操作从数据结构中抽象出来
{
public abstract void VisitConcreteElementA(ConcreteElementA concreteElementA); public abstract void VisitConcreteElementB(ConcreteElementB concreteElementB);
} public class ConcreteVisitor1 : Visitor //新增加操作, 只需要添加Visitor子类
{
public override void VisitConcreteElementA(ConcreteElementA concreteElementA)
{
//新操作对A的实现
} public override void VisitConcreteElementB(ConcreteElementB concreteElementB)
{
//新操作对B的实现
} } public class ConcreteElementA : Element
{
//所谓的双分派技术, 即先把操作类作为参数传入, 再把this作为参数传给操作类
//把具体操作细节给屏蔽了, 无论什么操作, 代码都不用变
public override void Accept(Visitor visitor)
{
visitor.VisitConcreteElementA(this);
} } //高层枚举类, 用来遍历执行Visitor操作
public class ObjectStructure
{
IList<Element> elements=new List<Element>(); public void Add(Element e)
{
elements.Add(e);
} public void Remove(Element e)
{
elements.Remove(e);
} public void Accept(Visitor visitor)
{
foreach (Element e in elements)
{
e.Accept(visitor);
}
}
} //客户代码
ObjectStructure os = new ObjectStructure();
os.Add(new ConcreteElementA());
os.Add(new ConcreteElementB()); ConcreteVisitor1 cv1 = new ConcreteVisitor1();
ConcreteVisitor2 cv2 = new ConcreteVisitor2(); //对所有的element分别执行cv1和cv2两种操作
os.Accept(cv1);
os.Accept(cv2);

 

其实访问者模式要解决的表达式问题, 参考Protocol and DataType

对于表达式问题的两个子问题,

1. 将已存在的方法扩展到新的类型, 比较容易实现, 通过继承可以实现

2. 为已存在的类型扩展新的方法,

对于这个问题, 面向对象是比较难于解决的和比较麻烦, 对于一般情况, 需要去每个类里面去添加该方法...

可以说, 访问者模式就是用于解决这个问题的, 当然是有代价的, 代价就是不支持1, 理由上面已经讲了, 除非这种操作对于所有的类的逻辑都是一样的, 但这种情况不太常见

所以, 面向对象无法完美的解决表达式问题, 就算采取访问者模式解决了子问题2, 但是代价就是牺牲了1

而可以看到, clojure就可以比较完美的解决这个问题

Design Pattern - 访问者模式的更多相关文章

  1. Thinking In Design Pattern——MVP模式演绎

    原文<Thinking In Design Pattern——MVP模式演绎>不知为何丢失了,故重新整理了一遍. 目录 What Is MVP Domain Model StubRepos ...

  2. Design Pattern - 命令模式

    一般执行一个操作的过程, 创建对象, 并调用对象的函数, 函数执行, 返回 比如下面的类图, client直接调用Receiver.action 而命令模式, 抽象出command对象, 并在comm ...

  3. 设计模式Design Pattern(4) -- 访问者模式

    什么是访问者模式? 一个对象有稳定的数据结构,却为不同的访问者提供不同的数据操作,对象提供接收访问者的方法,从而保证数据结构的稳定性和操作的多样性.也可以理解为,封装对象的操作方法,达到不改变对象数据 ...

  4. 深入浅出设计模式——访问者模式(Visitor Pattern)

    模式动机 对于系统中的某些对象,它们存储在同一个集合中,且具有不同的类型,而且对于该集合中的对象,可以接受一类称为访问者的对象来访问,而且不同的访问者其访问方式有所不同,访问者模式为解决这类问题而诞生 ...

  5. Scalaz(10)- Monad:就是一种函数式编程模式-a design pattern

    Monad typeclass不是一种类型,而是一种程序设计模式(design pattern),是泛函编程中最重要的编程概念,因而很多行内人把FP又称为Monadic Programming.这其中 ...

  6. 24种设计模式--访问者模式【Visitor Pattern】

    今天天气不错,绝对是晴空万里,骄阳似火呀,好,我们今天来讲访问者模式,我们在前面讲了组合模式和迭代器模式,通过组合模式我们能够把一个公司的人员组织机构树搭建起来,给管理带来非常大的便利,通过迭代器模式 ...

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

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

  8. 第23章 访问者模式(Visitor Pattern)

    原文 第23章 访问者模式(Visitor Pattern) 访问者模式 导读:访问者模式是我个人认为所有行为模式中最为复杂的一种模式了,这个模式可能看一遍会看不懂,我也翻了好几个例子,依然不能很好的 ...

  9. C#设计模式之二十二访问者模式(Visitor Pattern)【行为型】

    一.引言   今天我们开始讲"行为型"设计模式的第九个模式,该模式是[访问者模式],英文名称是:Visitor Pattern.如果按老规矩,先从名称上来看看这个模式,我根本不能获 ...

随机推荐

  1. 点滴积累【C#】---序列化和反序列化

    序列化和反序列化效果图: 序列化和反序列化代码: 需要添加两个命名空间: using System.IO; using System.Runtime.Serialization.Formatters. ...

  2. DDR 复位

    将FPGA代码和实际的数字电路对应起来. always @ (negedge clk_ref_200)     begin     if(ddr3_init_done)    'b10)   'b10 ...

  3. matplotlib之极坐标系的极径网格线(rgrids)的显示刻度

    matplotlib之极坐标系的极径网格线(rgrids)的显示刻度 #!/usr/bin/env python3 #-*- coding:utf-8 -*- #################### ...

  4. Linux之IO Redirection

    一.引言 前几天使用一个linux下的内存检测工具valgrind,想要把检测的结果重定向到文件,结果总是没有任何内容,最后才发现是重定向的原因,它输出的信息是输出到stderr的,所以我使用 > ...

  5. Highcharts 图表js框架

    纯js图表框架 ,图表传入Json数据 设置等等   , 如没特定要求可以考虑使用   优点 : 减轻服务器脚本运行负重  ,纯js执行,特效   缺点: 已知兼容性不高 帮助地址: http://w ...

  6. db_keep_cache_size參数的控制范围測试

    ocm考试新题中.须要创建keep存储的表,但在该參数是否应该改动上,有一些分歧.有人说asmm会自己主动给keep分配内存的,该參数就不用设置了. 看文档和asktom.也是云山雾罩,说什么的都有, ...

  7. liunx下安装tomcat7.0.82

    1.apache-tomcat-liunx-7.0.82下载地址: http://download.csdn.net/download/yichen01010/10019116 2.下载后解压即可 c ...

  8. python之函数cmp

    cpm函数是内置函数.可直接调用. cmp(x,y) 函数用于比较2个对象,如果 x < y 返回 -1, 如果 x == y 返回 0, 如果 x > y 返回 1. 但是,sorted ...

  9. SyncML 同步协议 感谢 周鹏(我只是做一个备份)

    SyncML 同步协议(SyncML Sync Protocol) 翻译周鹏 2006-1-24 摘要 本规范定义了SyncML客户和服务的同步协议. 它规范了怎样使用SynML表示层协议去完成Syn ...

  10. Spring MVC第一课:用IDEA构建一个基于Spring MVC, Hibernate, My SQL的Maven项目

    作为一个Spring MVC新手最基本的功夫就是学会如何使用开发工具创建一个完整的Spring MVC项目,本文站在一个新手的角度讲述如何一步一步创建一个基于Spring MVC, Hibernate ...