在现实生活中,有些集合对象存在多种不同的元素,且每种元素也存在多种不同的访问者和处理方式。例如,公园中存在多个景点,也存在多个游客,不同的游客对同一个景点的评价可能不同;医院医生开的处方单中包含多种药元素,査看它的划价员和药房工作人员对它的处理方式也不同,划价员根据处方单上面的药品名和数量进行划价,药房工作人员根据处方单的内容进行抓药。

这样的例子还有很多,例如,电影或电视剧中的人物角色,不同的观众对他们的评价也不同;还有顾客在商场购物时放在“购物车”中的商品,顾客主要关心所选商品的性价比,而收银员关心的是商品的价格和数量。

这些被处理的数据元素相对稳定而访问方式多种多样的数据结构,如果用“访问者模式”来处理比较方便。访问者模式能把处理方法从数据结构中分离出来,并可以根据需要增加新的处理方法,且不用修改原来的程序代码与数据结构,这提高了程序的扩展性和灵活性。

模式的定义与特点

访问者(Visitor)模式的定义:将作用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元素提供多种访问方式。它将对数据的操作与数据结构进行分离,是行为类模式中最复杂的一种模式。

访问者(Visitor)模式是一种对象行为型模式,其主要优点如下。

  1. 扩展性好。能够在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能。
  2. 复用性好。可以通过访问者来定义整个对象结构通用的功能,从而提高系统的复用程度。
  3. 灵活性好。访问者模式将数据结构与作用于结构上的操作解耦,使得操作集合可相对自由地演化而不影响系统的数据结构。
  4. 符合单一职责原则。访问者模式把相关的行为封装在一起,构成一个访问者,使每一个访问者的功能都比较单一。

访问者(Visitor)模式的主要缺点如下。

  1. 增加新的元素类很困难。在访问者模式中,每增加一个新的元素类,都要在每一个具体访问者类中增加相应的具体操作,这违背了“开闭原则”。
  2. 破坏封装。访问者模式中具体元素对访问者公布细节,这破坏了对象的封装性。
  3. 违反了依赖倒置原则。访问者模式依赖了具体类,而没有依赖抽象类。

模式的结构与实现

访问者(Visitor)模式实现的关键是如何将作用于元素的操作分离出来封装成独立的类,其基本结构与实现方法如下。

1. 模式的结构

访问者模式包含以下主要角色。

  1. 抽象访问者(Visitor)角色:定义一个访问具体元素的接口,为每个具体元素类对应一个访问操作 visit() ,该操作中的参数类型标识了被访问的具体元素。
  2. 具体访问者(ConcreteVisitor)角色:实现抽象访问者角色中声明的各个访问操作,确定访问者访问一个元素时该做什么。
  3. 抽象元素(Element)角色:声明一个包含接受操作 accept() 的接口,被接受的访问者对象作为 accept() 方法的参数。
  4. 具体元素(ConcreteElement)角色:实现抽象元素角色提供的 accept() 操作,其方法体通常都是 visitor.visit(this) ,另外具体元素中可能还包含本身业务逻辑的相关操作。
  5. 对象结构(Object Structure)角色:是一个包含元素角色的容器,提供让访问者对象遍历容器中的所有元素的方法,通常由 List、Set、Map 等聚合类实现。

其结构图如图 1 所示。

图1 访问者(Visitor)模式的结构图(点此查看原图)

2. 模式的实现

访问者模式的实现代码如下:

package net.biancheng.c.visitor;import java.util.*;public class VisitorPattern {public static void main(String[] args) {ObjectStructure os = new ObjectStructure();os.add(new ConcreteElementA());os.add(new ConcreteElementB());Visitor visitor = new ConcreteVisitorA();os.accept(visitor);System.out.println("------------------------");visitor = new ConcreteVisitorB();os.accept(visitor);}
}//抽象访问者
interface Visitor {void visit(ConcreteElementA element);void visit(ConcreteElementB element);
}//具体访问者A类
class ConcreteVisitorA implements Visitor {public void visit(ConcreteElementA element) {System.out.println("具体访问者A访问-->" + element.operationA());}public void visit(ConcreteElementB element) {System.out.println("具体访问者A访问-->" + element.operationB());}
}//具体访问者B类
class ConcreteVisitorB implements Visitor {public void visit(ConcreteElementA element) {System.out.println("具体访问者B访问-->" + element.operationA());}public void visit(ConcreteElementB element) {System.out.println("具体访问者B访问-->" + element.operationB());}
}//抽象元素类
interface Element {void accept(Visitor visitor);
}//具体元素A类
class ConcreteElementA implements Element {public void accept(Visitor visitor) {visitor.visit(this);}public String operationA() {return "具体元素A的操作。";}
}//具体元素B类
class ConcreteElementB implements Element {public void accept(Visitor visitor) {visitor.visit(this);}public String operationB() {return "具体元素B的操作。";}
}//对象结构角色
class ObjectStructure {private List<Element> list = new ArrayList<Element>();public void accept(Visitor visitor) {Iterator<Element> i = list.iterator();while (i.hasNext()) {((Element) i.next()).accept(visitor);}}public void add(Element element) {list.add(element);}public void remove(Element element) {list.remove(element);}
}

程序的运行结果如下:

具体访问者A访问-->具体元素A的操作。
具体访问者A访问-->具体元素B的操作。
------------------------
具体访问者B访问-->具体元素A的操作。
具体访问者B访问-->具体元素B的操作。

模式的应用场景

当系统中存在类型数量稳定(固定)的一类数据结构时,可以使用访问者模式方便地实现对该类型所有数据结构的不同操作,而又不会对数据产生任何副作用(脏数据)。

简而言之,就是当对集合中的不同类型数据(类型数量稳定)进行多种操作时,使用访问者模式。

通常在以下情况可以考虑使用访问者(Visitor)模式。

  1. 对象结构相对稳定,但其操作算法经常变化的程序。
  2. 对象结构中的对象需要提供多种不同且不相关的操作,而且要避免让这些操作的变化影响对象的结构。
  3. 对象结构包含很多类型的对象,希望对这些对象实施一些依赖于其具体类型的操作。

模式的扩展

访问者(Visitor)模式是使用频率较高的一种设计模式,它常常同以下两种设计模式联用。

(1)与“迭代器模式”联用。因为访问者模式中的“对象结构”是一个包含元素角色的容器,当访问者遍历容器中的所有元素时,常常要用迭代器。如【例1】中的对象结构是用 List 实现的,它通过 List 对象的 Iterator() 方法获取迭代器。如果对象结构中的聚合类没有提供迭代器,也可以用迭代器模式自定义一个。

(2)访问者(Visitor)模式同“组合模式”联用。因为访问者(Visitor)模式中的“元素对象”可能是叶子对象或者是容器对象,如果元素对象包含容器对象,就必须用到组合模式,其结构图如图 4 所示。

图4 包含组合模式的访问者模式的结构图

进阶阅读

如果您想深入了解访问者模式,可猛击阅读以下文章。

  • 《访问者模式的伪动态双分派》
  • 《访问者模式在JDK源码中的应用》
  • 《访问者模式在Spring源码中的应用》

访问者模式(Visitor模式)相关推荐

  1. 设计模式之访问者模式(Visitor)

    访问者模式(Visitor) 在访问者模式(Visitor Pattern)中,我们使用了一个访问者类,它改变了元素类的执行算法.通过这种方式,元素的执行算法可以随着访问者改变而改变 主要将数据结构与 ...

  2. k8s里的Visitor模式学习

    Visitor模式 visitor模式在k8s源代码里面比较场景,刚接触,理解起来有点绕,体会其本质后,发现就是callback的一种实现 示例代码 type Callback func()type ...

  3. 设计模式之访问者模式(Visitor)摘录

    23种GOF设计模式一般分为三大类:创建型模式.结构型模式.行为模式. 创建型模式抽象了实例化过程,它们帮助一个系统独立于如何创建.组合和表示它的那些对象.一个类创建型模式使用继承改变被实例化的类,而 ...

  4. 设计模式:访问者(Visitor)模式

    设计模式:访问者(Visitor)模式 一.前言    什么叫做访问,如果大家学过数据结构,对于这点就很清晰了,遍历就是访问的一般形式,单独读取一个元素进行相应的处理也叫作访问,读取到想要查看的内容+ ...

  5. 设计模式之访问者(visitor)模式

    在患者就医时,医生会根据病情开具处方单,很多医院都会存在以下这个流程:划价人员拿到处方单之后根据药品名称和数量计算总价,而药房工作人员根据药品名称和数量准备药品,如下图所示. 在软件开发中,有时候也需 ...

  6. 设计模式:访问者模式(Visitor Pattern)

    访问者模式(Visitor Pattern): 封装一些作用于某种数据结构的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作. 主要将数据结构与数据操作分离,解决数据结构和操作 ...

  7. 访问者(Visitor)模式

    访问者(Visitor)模式:访问者模式的目的是封装一些施加于某种数据结构元素之上的操作.一旦这些操作需要修改的话,接受这个操作的数据结构则可以保持不变. /* * 抽象访问者(Visitor)角色: ...

  8. c++访问者模式visitor

    c++访问者模式visitor 概念 角色和职责 优缺点 案例 概念 Visitor模式也叫访问者模式,是行为模式之一,它分离对象的数据和行为,使用Visitor模式,可以不修改已有类的情况下,增加新 ...

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

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

  10. 40访问者模式(Visitor Pattern)

    类层次结构的变化:     类层次结构中可能经常由于引入新的操作,从而将类型变得脆弱...                             动机:     在软件构建过程中,由于需求的改变,某 ...

最新文章

  1. Windows系统更新问题汇总(补充修改)
  2. 重磅盘点!过去一年里最受欢迎的技术干货,全在这里了
  3. c语言 link找不到函数,link失败,找不到库函数'sqrtf',math.h的头文件已经包含...
  4. python自动卸载win程序_利用python实现自动扫雷程序
  5. ArrayList 类方法toArray的一点疑惑
  6. CAS单点登陆,URL多出个参数jsessionid导致登陆失败问题
  7. linux内存源码分析 - 伙伴系统(初始化和申请页框)
  8. 张效详java就业培训教程学习笔记(三)
  9. 计算机组成原理期末复习【超实用】
  10. Unity播放服务器端视频 发布到Android移动端
  11. cad工具箱详细讲解_CAD贱人工具箱的使用教程详解
  12. 重磅!100位校高校教师晒工资,详细晒,全国各地!要进高校的博士们参考
  13. 安装ansys时出现问题 MS.NET Framework would you like to retry?的解决办法:
  14. 通过爬虫获取免费IP代理,搭建自己的IP池(https)
  15. 免校准的电量计量芯片_【应用】基于高精度免校准电能计量芯片CSE7761的漏电保护设计,可支持单芯片两路计量...
  16. 俞敏洪在清华励志演讲
  17. 使用 Javascript 与 Flow 交互
  18. QT5显示视频或者图片缩小后以滚动条方式显示
  19. 冲破测试职业天花板--《笑傲测试》读后感
  20. 7.RT-thread 项目实战--FreeModbus协议的移植

热门文章

  1. vbs脚本在服务器上虚拟按键,怎么用VBS代码实现模拟键盘按键?
  2. oracle 折旧公式,oracle FA_资产折旧 1.1.doc
  3. Selenium原理以及Python从零实现
  4. 阿里云天池SQL训练营学习记录
  5. php递归函数return问题
  6. 解决-最新版Google谷歌浏览器上传下载卡死无响应-问题
  7. CORBA协议相关的概念
  8. 视频教程-计算机网络技术-网络技术
  9. FXCM福汇官网 fx-aisa.com外汇交易中,你必须了解的八种主流货币知识
  10. batch文件常用命令