访问者模式(Visitor Pattern)指将作用域某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作。借用《Java设计模式》中的例子说明:在医院医生开具药单后,划价人员拿到药单后会根据药单上的药品名称和数量计算总价,而药房工作人员则根据药品名称和数量准备药品。如下图所示:

那么药品处方可以看成是一个药品信息的集合,里面包含了一种或多种不同类型的药品信息,不同类型的工作人员在操作统一药品信息集合时将提供不同的处理方式,而且可能还会增加新类型的工作人员来操作处方单。这就是访问者模式的典型应用场景。

一、访问者模式介绍

1.1 访问者模式的结构

访问者模式是一种较为复杂的行为型模式,它包含访问者(Visitor)和被访问元素(Element)两个主要组成部分。下面就来看看访问者模式的具体结构:

  • Visitor:抽象访问者,为对象结构中的每个具体元素类声明一个访问操作
  • ConcreteVisitor1、ConcreteVisitor2:具体访问者,实现抽象访问者声明的操作
  • Element:抽象元素,定义一个accept()方法
  • ConcreteElement1、ConcreteElement2:具体元素,实现抽象元素中的accept()方法,在accept()方法中调用访问者的访问方法以便完成对一个元素的操作。
  • ObjectStructure:对象结构,它是一个元素的集合,用于存放元素对象,并且提供了遍历其内部元素的方法。
  • Client:客户端

1.2 访问者模式的实现

根据上面的类图,首先是抽象访问者,为每一种具体类型对象都会提供一个访问方法:

public interface Visitor {void visit(ConcreteElementA elementA);void visit(ConcreteElementB elementB);
}

接下来是具体访问者,实现抽象访问者的声明方法

public class ConcreteVisitor1 implements Visitor{@Overridepublic void visit(ConcreteElementA elementA) {System.out.println("ConcreteVisitor1 访问 ConcreteElementA: " + elementA.operationA());}@Overridepublic void visit(ConcreteElementB elementB) {System.out.println("ConcreteVisitor1 访问 ConcreteElementB: " + elementB.operationB());}
}
public class ConcreteVisitor2 implements Visitor {@Overridepublic void visit(ConcreteElementA elementA) {System.out.println("ConcreteVisitor2 访问 ConcreteElementA: " + elementA.operationA());}@Overridepublic void visit(ConcreteElementB elementB) {System.out.println("ConcreteVisitor2 访问 ConcreteElementB: " + elementB.operationB());}
}

然后是抽象元素接口,定义一个accept()方法,用于接受访问者的访问:

public interface Element {void accept(Visitor visitor);
}

下面是实现抽象元素接口的具体元素类,除了重载accept()方法,还实现对应的具体操作方法

public class ConcreteElementA implements Element{@Overridepublic void accept(Visitor visitor) {visitor.visit(this);}public String operationA() {return "ConcreteElementA的操作方法";}
}
public class ConcreteElementB implements Element{@Overridepublic void accept(Visitor visitor) {visitor.visit(this);}public String operationB() {return "ConcreteElementB的操作方法";}
}

最后是对象结构类,是一个对元素进行操作的容器,提供访问者遍历容器中所有元素的方法

public class ObjectStructure {private List<Element> elementList = new ArrayList<>();public void accept(Visitor visitor) {Iterator<Element> it = elementList.iterator();while(it.hasNext()) {it.next().accept(visitor);}}public void add(Element element) {elementList.add(element);}public void remove(Element element) {elementList.remove(element);}
}

客户端测试类:

public class Client {public static void main(String[] args) {//将具体元素注入对象结构中ObjectStructure objectStructure = new ObjectStructure();objectStructure.add(new ConcreteElementA());objectStructure.add(new ConcreteElementB());//具体访问者访问具体元素objectStructure.accept(new ConcreteVisitor1());objectStructure.accept(new ConcreteVisitor2());}
}

测试结果:

ConcreteVisitor1 访问 ConcreteElementA: ConcreteElementA的操作方法
ConcreteVisitor1 访问 ConcreteElementB: ConcreteElementB的操作方法
ConcreteVisitor2 访问 ConcreteElementA: ConcreteElementA的操作方法
ConcreteVisitor2 访问 ConcreteElementB: ConcreteElementB的操作方法

二、访问者模式的应用场景

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

  • 一个对象结构中包含多个类型的对象,希望对这些对象实施一些依赖其具体类型的操作
  • 需要对一个对象结构中的对象进行很多不同的并且不相关的操作
  • 对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作

三、访问者模式实战

本案例模拟学校中学生和老师对于不同用户的访问视角(案例来源于《重学Java设计模式》)

这个案例场景我们模拟校园中有学⽣和⽼师两种身份的⽤户,那么对于家⻓和校⻓关⼼的⻆度来看,他 们的视⻆是不同的。家⻓更关⼼孩⼦的成绩和⽼师的能⼒,校⻓更关⼼⽼师所在班级学⽣的⼈数和升学 率

从前面第一节的结构图和实现代码就可以知道,访问者模式的整体类结构相对复杂,下面就来看看该案例的核心逻辑实现:

  1. 需要建立用户抽象类和抽象访问方法,再由不同的用户实现(相当于前面的元素),这里的用户指看老师和学生;
  2. 建立访问者接口,用于不同人员的访问操作,这里的访问者指校长和家长;
  3. 最终是对数据的看板建设,用于实现不同视角的访问结果输出(相当于前面的对象结构);

具体代码实现

  1. 用户抽象类及具体实现类

先来看看用户抽象类,类似于第一节中的抽象元素类

public abstract class User {/**姓名*/public String name;/**用户的身份,包括学生和教师*/public String identity;/**所属班级*/public String clazz;public User(String name, String identity, String clazz) {this.name = name;this.identity = identity;this.clazz = clazz;}public abstract void accept(Visitor visitor);
}

具体用户类,包括学生和老师,每个具体用户可以实现对应不同的方法,比如学生的排名,老师的升学率方法。

public class Student extends User{public Student(String name, String identity, String clazz) {super(name, identity, clazz);}@Overridepublic void accept(Visitor visitor) {visitor.visit(this);}public int ranking() {return (int) (Math.random()*100);}
}
public class Teacher extends User {public Teacher(String name, String identity, String clazz) {super(name, identity, clazz);}@Overridepublic void accept(Visitor visitor) {visitor.visit(this);}//升学率public double entranceRatio() {return BigDecimal.valueOf(Math.random() * 100).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();}}
  1. 抽象访问者及具体实现类

先看看抽象访问者接口:

public interface Visitor {void visit(Student student);void visit(Teacher teacher);
}

对应的具体访问者实现,包括父母和校长类。不同访问者的访问角度也不相同,校长看重教师的升学率,父母看重学生的排名

public class Parent implements Visitor{private Logger logger = LoggerFactory.getLogger(Parent.class);@Overridepublic void visit(Student student) {logger.info("学生信息 姓名:{} 班级:{} 排名:{}", student.name, student.clazz, student.ranking());}@Overridepublic void visit(Teacher teacher) {logger.info("老师信息 姓名:{} 班级:{}", teacher.name, teacher.clazz);}
}
public class Principal implements Visitor{private Logger logger = LoggerFactory.getLogger(Principal.class);@Overridepublic void visit(Student student) {logger.info("学生信息 姓名: {} 班级:{}", student.name, student.clazz);}@Overridepublic void visit(Teacher teacher) {logger.info("老师信息 姓名: {} 班级:{} 升学率:{}", teacher.name, teacher.clazz, teacher.entranceRatio());}
}
  1. 数据看板

数据看板就类似于第一节中的对象结构(ObjectStructure),初始化具体用户的信息,展示访问者的访问角度信息:

public class DataView {List<User> userList = new ArrayList<>();public DataView() {userList.add(new Student("Ethan", "普通班", "高一1班"));userList.add(new Student("Tom", "重点班", "高一2班"));userList.add(new Student("Peter", "重点班", "高一3班"));userList.add(new Teacher("张三", "普通班", "高一1班"));userList.add(new Teacher("李四", "重点班", "高一2班"));userList.add(new Teacher("王五", "重点班", "高一3班"));}public void show(Visitor visitor) {for (User user : userList) {user.accept(visitor);}}
}
  1. 测试类

对整个流程进行测试:

public class ApiTest {private Logger logger = LoggerFactory.getLogger(ApiTest.class);@Testpublic void test() {DataView dataView = new DataView();logger.info("家长视角访问:");dataView.show(new Parent());logger.info("校长视角访问:");dataView.show(new Principal());}
}

结果为:

12:27:18.983 [main] INFO  ApiTest - 家长视角访问:
12:27:18.983 [main] INFO  visitor.Parent - 学生信息 姓名:Ethan 班级:高一1班 排名:22
12:27:18.983 [main] INFO  visitor.Parent - 学生信息 姓名:Tom 班级:高一2班 排名:2
12:27:18.983 [main] INFO  visitor.Parent - 学生信息 姓名:Peter 班级:高一3班 排名:0
12:27:18.983 [main] INFO  visitor.Parent - 老师信息 姓名:张三 班级:高一1班
12:27:18.983 [main] INFO  visitor.Parent - 老师信息 姓名:李四 班级:高一2班
12:27:18.983 [main] INFO  visitor.Parent - 老师信息 姓名:王五 班级:高一3班
12:27:18.983 [main] INFO  ApiTest - 校长视角访问:
12:27:18.983 [main] INFO  visitor.Principal - 学生信息 姓名: Ethan 班级:高一1班
12:27:18.983 [main] INFO  visitor.Principal - 学生信息 姓名: Tom 班级:高一2班
12:27:18.983 [main] INFO  visitor.Principal - 学生信息 姓名: Peter 班级:高一3班
12:27:18.998 [main] INFO  visitor.Principal - 老师信息 姓名: 张三 班级:高一1班 升学率:39.85
12:27:18.998 [main] INFO  visitor.Principal - 老师信息 姓名: 李四 班级:高一2班 升学率:88.14
12:27:18.998 [main] INFO  visitor.Principal - 老师信息 姓名: 王五 班级:高一3班 升学率:44.65

参考资料

《Java设计模式》

《重学Java设计模式》

http://c.biancheng.net/view/1397.html

访问者模式及其应用场景相关推荐

  1. 【设计模式】访问者模式 ( 简介 | 适用场景 | 优缺点 | 代码示例 )

    文章目录 一.访问者模式简介 二.访问者模式 适用场景 三.访问者模式 优缺点 四.访问者模式 与 迭代器模式 五.代码示例 1.Game 父类 ( 被访问者 ) 2.VipGame 收费游戏 ( 被 ...

  2. java的string访问某个元素_C#深究.net常用的23种设计模式之访问者模式(Vistor Pattern)...

    一.引言 在上一篇博文中分享了责任链模式,责任链模式主要应用在系统中的某些功能需要多个对象参与才能完成的场景.在这篇博文中,我将为大家分享我对访问者模式的理解. 二.访问者模式介绍 2.1 访问者模式 ...

  3. 23种设计模式之访问者模式

    访问者模式的定义 定义: 封装一些作用于某种数据结构中的各元素的操作, 它可以在不改变数据结构的前提下定义作用于这些元素的新的操作 通俗的说, 就是定义一个访问者角色, 当对指定角色进行访问时要通过访 ...

  4. Java 设计模式之 Visitor 访问者模式

    Visitor 访问者模式适用的场景是,存在某个容器,里面的组成部分不会发生变化,对这些组成部分的访问,不是由这个容器决定,而是交给访问者决定. 举个生活中的例子,比如稻草人,卖包,卖鞋子这些固定的消 ...

  5. 第 18 章 访问者模式

    第 18 章 访问者模式 1.测评系统的需求 完成测评系统需求 将观众分为男人和女人,对歌手进行测评,当看完某个歌手表演后,得到他们对该歌手不同的评价(评价有不同的种类,比如成功.失败等) 2.传统方 ...

  6. 设计模式——访问者模式

    访问者模式 访问者模式(Visitor Pattern):封装一些作用于某种数据结构中的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作. 访问者模式就是根据朋友的信息,执行了 ...

  7. C#设计模式(22)——访问者模式(Vistor Pattern)

    一.引言 在上一篇博文中分享了责任链模式,责任链模式主要应用在系统中的某些功能需要多个对象参与才能完成的场景.在这篇博文中,我将为大家分享我对访问者模式的理解. 二.访问者模式介绍 2.1 访问者模式 ...

  8. 设计模式:访问者模式-vistor

    表示一个作用于某对象结构中的各元素的操作,它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作 访问者模式的优点 1.符合单一职责原则:凡是适用访问者模式的场景中,元素类中需要封装在访问者中 ...

  9. C#设计模式——访问者模式(Vistor Pattern)

    一.引言 在上一篇博文中分享了责任链模式,责任链模式主要应用在系统中的某些功能需要多个对象参与才能完成的场景.在这篇博文中,我将为大家分享我对访问者模式的理解. 二.访问者模式介绍 2.1 访问者模式 ...

最新文章

  1. AI 技术发展飞快,高校 AI 教师的知识储备能满足学生旺盛的求知欲吗?
  2. 我在兰亭这三年之自动化框架升级
  3. 5008.工程师职场能力自测评估
  4. 网络通信应用开发利器!—— ESPlus —— ESFramework通信框架的增强库
  5. 如何搭建一个 MySQL 分布式集群
  6. python的内存分配
  7. [vue] 动态给vue的data添加一个新的属性时会发生什么?怎样解决?
  8. 正则表达式匹配多个字符串中的一个
  9. 自己做计算机三级,计算机三级辅导:自己“做”软驱
  10. 剑指offer例题分享--7
  11. UGUI动态改变MatchWidthOrHeight
  12. mysql基本概念之关系_Mysql数据库学习(一):数据库基本概念、关系型数据库、Mysql数_MySQL...
  13. 如何去掉版块前边的有无新帖图标
  14. asp中 打开网页时出现“操作必须使用一个可更新的查询”原因及解决办法
  15. 红外线体温枪制作方案
  16. 一纸读懂另类数据 | 未央研究
  17. Windows7 UAC 实验
  18. 江西 南昌 富士康 java,好消息!富士康智能科技小镇落户小蓝经开区!总投资达110亿元...
  19. uefi怎么念_UEFI是什么,看完您就全明白了
  20. Xilinx IOBUF 的用法

热门文章

  1. 网络协议及网络软件框架设计网络协议
  2. 网络入侵的几种常用方法
  3. python 实现将图像转化为位图数组
  4. 深入探讨:标签(Tag)的各种设计方案
  5. 《机电传动控制》第一周作业
  6. python一元线性回归
  7. 模平方剩余(二次剩余)与欧拉判别法
  8. 关于css媒体查询不起作用的解决方法
  9. 使用 hostapd 轻松实现强 WiFi 加密
  10. python制作表情包教程_使用Python制作表情包实现换脸功能