北风设计模式课程---访问者(Visitor)模式
北风设计模式课程---访问者(Visitor)模式
一、总结
一句话总结:
设计模式是日常问题的经验总结方案,所以学好设计模式对日常出现的问题可以有很好的解决。
访问者设计模式有点神似 抽象工厂模式,都是增加一个维度很容易,增加另外一个维度很麻烦
1、访问者设计模式 为什么 叫访问者设计模式?
1、访问是什么:访问就是遍历
2、增加遍历方式修改类犯了oop的开闭原则
3、将访问独立出来变成一个新的类
什么叫做访问,如果大家学过数据结构,对于这点就很清晰了,遍历就是访问的一般形式,单独读取一个元素进行相应的处理也叫作访问,读取到想要查看的内容+对其进行处理就叫做访问,那么我们平常是怎么访问的,基本上就是直接拿着需要访问的地址(引用)来读写内存就可以了。
为什么还要有一个访问者模式呢,这就要放到OOP之中了,在面向对象编程的思想中,我们使用类来组织属性,以及对属性的操作,那么我们理所当然的将访问操作放到了类的内部,这样看起来没问题,但是当我们想要使用另一种遍历方式要怎么办呢,我们必须将这个类进行修改,这在设计模式中是大忌,在设计模式中就要保证,对扩展开放,对修改关闭的开闭原则。
因此,我们思考,可不可以将访问操作独立出来变成一个新的类,当我们需要增加访问操作的时候,直接增加新的类,原来的代码不需要任何的改变,如果可以这样做,那么我们的程序就是好的程序,因为可以扩展,符合开闭原则。而访问者模式就是实现这个的,使得使用不同的访问方式都可以对某些元素进行访问。
2、访问者设计模式中 最核心的类是什么?
ObjectStructure(对象结构):用来存储被访问者的集合,可以循环遍历执行 被访问者的Accept方法:比如财务处或者人事处访问所有员工(全职和临时员工)
3、访问者模式中为什么 被访问者 里面都有个accept方法?
当不同访问者过来访问的时候,我只能接受(accept)别人的访问
4、访问者模式的优缺点是什么?
增加访问者很简单,增加 被访问者麻烦,需要在每一个访问者类中增加相应访问操作代码 。
5、访问者模式总结?
访问者模式是一个非常有意思的模式,因为自己需要得到数据就需要向被访者索取,如果能够一次索取成功,访问就结束了,如果还需要其他信息,则再次向被访问者索取,就这样知道拿到自己需要的所有数据。
6、访问者模式和迭代器模式 异同?
访问者模式和迭代器模式都是在某种数据结构上进行处理,一种是对数据结构中的元素进行某种特定的处理,另一种是用某种方式遍历所有元素。
二、设计模式:访问者(Visitor)模式
转自或参考:设计模式:访问者(Visitor)模式
https://www.cnblogs.com/zyrblog/p/9244754.html
一、前言
什么叫做访问,如果大家学过数据结构,对于这点就很清晰了,遍历就是访问的一般形式,单独读取一个元素进行相应的处理也叫作访问,读取到想要查看的内容+对其进行处理就叫做访问,那么我们平常是怎么访问的,基本上就是直接拿着需要访问的地址(引用)来读写内存就可以了。
为什么还要有一个访问者模式呢,这就要放到OOP之中了,在面向对象编程的思想中,我们使用类来组织属性,以及对属性的操作,那么我们理所当然的将访问操作放到了类的内部,这样看起来没问题,但是当我们想要使用另一种遍历方式要怎么办呢,我们必须将这个类进行修改,这在设计模式中是大忌,在设计模式中就要保证,对扩展开放,对修改关闭的开闭原则。
因此,我们思考,可不可以将访问操作独立出来变成一个新的类,当我们需要增加访问操作的时候,直接增加新的类,原来的代码不需要任何的改变,如果可以这样做,那么我们的程序就是好的程序,因为可以扩展,符合开闭原则。而访问者模式就是实现这个的,使得使用不同的访问方式都可以对某些元素进行访问。

二、代码
Element 接口:
package zyr.dp.visitor;
public interface Element {
public abstract void accept(Visitor visitor);
}
Entry 类:
package zyr.dp.visitor;
import java.util.Iterator;
public abstract class Entry implements Element{
public abstract String getName();
public abstract int getSize();
public abstract void printList(String prefix);
public void printList(){
printList("");
}
public Entry add(Entry entry) throws RuntimeException{
throw new RuntimeException();
}
public Iterator iterator() throws RuntimeException{
throw new RuntimeException();
}
public String toString(){
return getName()+"<"+getSize()+">";
}
}
File 类:
package zyr.dp.visitor;
public class File extends Entry {
private String name;
private int size;
public File(String name,int size){
this.name=name;
this.size=size;
}
public String getName() {
return name;
}
public int getSize() {
return size;
}
public void printList(String prefix) {
System.out.println(prefix+"/"+this);
}
public void accept(Visitor visitor) {
// System.out.println("开始访问文件:"+this);
visitor.visit(this);
// System.out.println("结束访问文件:"+this);
// System.out.println();
}
}
Directory类:
package zyr.dp.visitor; import java.util.ArrayList;
import java.util.Iterator; public class Directory extends Entry { String name;
ArrayList entrys=new ArrayList();
public Directory(String name){
this.name=name;
}
public String getName() {
return name;
} public int getSize() {
int size=0;
Iterator it=entrys.iterator();
while(it.hasNext()){
size+=((Entry)it.next()).getSize();
}
return size;
} public Entry add(Entry entry) {
entrys.add(entry);
return this;
} public Iterator iterator() {
return entrys.iterator();
} public void printList(String prefix) {
System.out.println(prefix+"/"+this);
Iterator it=entrys.iterator();
Entry entry;
while(it.hasNext()){
entry=(Entry)it.next();
entry.printList(prefix+"/"+name);
}
}
public void accept(Visitor visitor) {
// System.out.println("开始访问文件夹:"+this);
visitor.visit(this);
// System.out.println("结束访问文件夹:"+this);
// System.out.println();
} }
Visitor 类:
package zyr.dp.visitor;
public abstract class Visitor {
public abstract void visit(File file);
public abstract void visit(Directory directory);
}
ListVisitor类:
package zyr.dp.visitor;
import java.util.Iterator;
public class ListVisitor extends Visitor {
String currentDir = "";
public void visit(File file) {
System.out.println(currentDir+"/"+file);
}
public void visit(Directory directory) {
System.out.println(currentDir+"/"+directory);
String saveDir=currentDir;
currentDir+=("/"+directory.getName());
Iterator it=directory.iterator();
while(it.hasNext()){
Entry entry=(Entry)it.next();
entry.accept(this);
}
currentDir=saveDir;
}
}
FileVisitor 类:
package zyr.dp.visitor; import java.util.ArrayList;
import java.util.Iterator; public class FileVisitor extends Visitor { String currentDir = "";
String suffix;
ArrayList files=new ArrayList(); public FileVisitor(String suffix){
this.suffix = suffix;
} public void visit(File file) {
if(file.getName().endsWith(suffix)){
// System.out.println(currentDir+"/"+file);
files.add(currentDir+"/"+file);
}
} public void visit(Directory directory) {
String saveDir=currentDir;
currentDir+=("/"+directory.getName());
Iterator it=directory.iterator();
while(it.hasNext()){
Entry entry=(Entry)it.next();
entry.accept(this);
}
currentDir=saveDir;
}
Iterator getFiles(){
return files.iterator();
} }
Main类:
package zyr.dp.visitor;
import java.util.Iterator;
public class Main {
public static void main(String[] args) {
Directory root=new Directory("根目录");
Directory life=new Directory("我的生活");
File eat=new File("吃火锅.txt",100);
File sleep=new File("睡觉.html",100);
File study=new File("学习.txt",100);
life.add(eat);
life.add(sleep);
life.add(study);
Directory work=new Directory("我的工作");
File write=new File("写博客.doc",200);
File paper=new File("写论文.html",200);
File homework=new File("写家庭作业.docx",200);
work.add(write);
work.add(paper);
work.add(homework);
Directory relax=new Directory("我的休闲");
File music=new File("听听音乐.js",200);
File walk=new File("出去转转.psd",200);
relax.add(music);
relax.add(walk);
Directory read=new Directory("我的阅读");
File book=new File("学习书籍.psd",200);
File novel=new File("娱乐小说.txt",200);
read.add(book);
read.add(novel);
root.add(life);
root.add(work);
root.add(relax);
root.add(read);
root.accept(new ListVisitor());
System.out.println("========================");
FileVisitor visitor=new FileVisitor(".psd");
root.accept(visitor);
Iterator it = visitor.getFiles();
while(it.hasNext()){
System.out.println(it.next());
}
}
}
运行结果:

可以看到我们的运行结果第一个和使用Composite模式的结果一样,第二个是实现另一种方式的访问,只访问文件后缀为某一特定的内容的文件,结果也是正确的,并且为了说明我们的访问还可以保存下来访问的结果,我们使用了ArrayList自带的迭代器将保存到ArrayList中的结果输出出来,我们当然也可以直接在遍历的时候就输出出来,这个看我们的使用要求了。由此可以看到在保证数据结构(File和Directory)不发生变化的情况下(没有新增或者删除),可以非常方便增加新的一种访问方法,只需要新增加一个访问类即可,但是如果我们数据结构发生变化之后,就需要修改继承自Visitor类的所有类了,这也违背了开闭原则,因此我们应该认真考虑,到底我们的数据结构是定死的还是经常变化的。没有任何一种设计模式是十全十美的,总是有所取舍,有所利弊,根据实际情况来选择才是最好的设计方法。
这里要说明一下双重分发机制,我们来看一下最核心的遍历逻辑,结合组合模式的时候我们已经分析过的遍历方法,递归,大家觉得这次我们要怎么在数据结构外面进行遍历,肯定还是要使用递归了,可是数据结构中的数据在类的内部,怎么递归到内部呢,我们想到了间接递归,也就是双重分发。
public void printList(String prefix) {
System.out.println(prefix+"/"+this);
Iterator it=entrys.iterator();
Entry entry;
while(it.hasNext()){
entry=(Entry)it.next();
entry.printList(prefix+"/"+name);
}
}
上面的代码是在组合模式类的内部遍历的过程,可以明确的看到递归(直接递归)的使用。我们看一下访问者模式中的间接递归:
Directory类中:
public void accept(Visitor visitor) {
//System.out.println("开始访问文件夹:"+this);
visitor.visit(this);
//System.out.println("结束访问文件夹:"+this);
//System.out.println();
}
File类:
public void accept(Visitor visitor) {
//System.out.println("开始访问文件:"+this);
visitor.visit(this);
//System.out.println("结束访问文件:"+this);
//System.out.println();
}
在ListVisitor中:
1 public void visit(File file) {
2 System.out.println(currentDir+"/"+file);
3 }
public void visit(Directory directory) {
System.out.println(currentDir+"/"+directory);
String saveDir=currentDir;
currentDir+=("/"+directory.getName());
Iterator it=directory.iterator();
while(it.hasNext()){
Entry entry=(Entry)it.next();
entry.accept(this);
}
currentDir=saveDir;
}
我们看到了entry.accept(this)这句话,这句话是非常重要的,我们在Main中是这样用的:
root.accept(new ListVisitor());
那么串连起来,在Main中我们通过Directory或者File类型的对象调用accept(访问者)方法,接受访问者的访问,这是访问者和被访问者的第一次亲密接触,亲近对方就是为了获得对方的数据,然后才能对对方的数据进行使用,那么怎么拿到的呢?!我们看到了这句visitor.visit(this);这句话无疑是重要的,被调用者告诉访问者,我将我的内容this,全部给你了,以后访问者就可以对this所指代的被访问者的内容进行操作了,分为两类,如果被访问者是File文件类型的,就会直接输出内容,到达叶子结点,访问结束;如果是文件夹,那就非常有意思了,首先我们仍旧是让被访问者将自己的内容交给访问者visitor.visit(this);,之后public void visit(Directory directory)被调用,通过遍历的方式将属于这个文件夹下面的数据全部拿到Iterator it=directory.iterator();,然后开始一个个的处理,怎么处理呢,继续访问属于这个文件夹下面对象的accept()方法使用entry.accept(this);,来将访问者交过去,交给谁?!肯定是给entry所指的对象,也就是文件夹里面的子文件夹或者文件,如果是文件的话,继续在自己的方法中调用visitor.visit(this);,最终落实到调用 public void visit(File file)通过System.out.println(currentDir+"/"+file);访问结束,如果不是文件呢?若为文件夹,则继续调用属于文件夹的方法,就这样不断地往下面查找,一直到遍历完文件夹下面的所有的元素,因此也是深度优先遍历。就这样通过压栈和出栈,我们完成了最终的遍历,最终的出口有两个,一个是访问文件,输出之后结束,另一个是遍历完文件夹,即使文件夹下面没有文件依旧结束。
root.accept(new ListVisitor());
public void accept(Visitor visitor) {
visitor.visit(this);
}
public void visit(File file) {
System.out.println(currentDir+"/"+file);
}
public void visit(Directory directory) {
System.out.println(currentDir+"/"+directory);
String saveDir=currentDir;
currentDir+=("/"+directory.getName());
Iterator it=directory.iterator();
while(it.hasNext()){
Entry entry=(Entry)it.next();
entry.accept(this);
}
currentDir=saveDir;
}
在accept函数中调用visit,同样在visit中调用accept,这就是间接递归,或者叫做双重分发。产生的原因就是访问者需要和被访问者相互交流,才能一步步的得到想要的数据。我们可以考虑主持人采访一个明星,那么这个明星接受采访,把自己基本信息(能问的问题以及某些答案)告诉主持人,问主持人有问题吗?如果主持人有问题(还能向下问)要问那么就再次拿着新的问题问这个明星,这个明星再次将自己关于这方面的信息告诉主持人;如果没有问题(得到答案),主持人将信息总结之后说出来。就这样一直持续下去,直到主持人没问题问了,并且明星的信息也都被问到了,这样采访就结束了。由此可见,很多时候设计模式都是和生活密切相关的,生活中的常识有时候就是一些套路,而这种套路就是一种抽象的模式。
三、总结
访问者模式是一个非常有意思的模式,因为自己需要得到数据就需要向被访者索取,如果能够一次索取成功,访问就结束了,如果还需要其他信息,则再次向被访问者索取,就这样知道拿到自己需要的所有数据。在本例中借用了组合模式中的数据结构,那是因为这种树形的结构很适合我们进行递归访问。访问者模式和迭代器模式都是在某种数据结构上进行处理,一种是对数据结构中的元素进行某种特定的处理,另一种是用某种方式遍历所有元素。在实际应用中,我们根据实际需要来考虑是不是需要双重分发机制。在本例中的访问者模式中用到了组合模式、委托(组合)、双重分发等原理,便于新增访问方式,不便于对数据结构的修改。
北风设计模式课程---访问者(Visitor)模式的更多相关文章
- 北风设计模式课程---访问者模式(Visitor)
北风设计模式课程---访问者模式(Visitor) 一.总结 一句话总结: 设计模式是日常问题的经验总结方案,所以学好设计模式对日常出现的问题可以有很好的解决. 访问者设计模式有点神似 抽象工厂模式, ...
- 设计模式-访问者(Visitor)模式
访问者模式是对象的行为模式.访问者模式的目的是封装施加在某种数据结构元素上的操作.一旦一些操作需要修改,接受这个操作的数据结构可以保持不变. 个人觉得访问者模式相对其他的设计模式来说稍微复杂,难理解一 ...
- [设计模式]访问者 Visitor 模式
访问者模式是对象的行为模式. 访问者模式的目的是封装一些施加于某种数据结构元素之上的操作.一旦这些操作需要修改的话,接受这个操作的数据结构则可以保持不变.
- 北风设计模式课程---状态模式State(对象行为型)
北风设计模式课程---状态模式State(对象行为型) 一.总结 一句话总结: 状态模式 具体状态的行为在具体的状态类中就解决,不用交给外部做判断.实质是将多条件判断弄成了多个类,在不同的类中做判断 ...
- 北风设计模式课程---备忘录(Memento)模式
北风设计模式课程---备忘录(Memento)模式 一.总结 一句话总结: 备忘录模式也是一种比较常用的模式用来保存对象的部分用于恢复的信息,和原型模式有着本质的区别,广泛运用在快照功能之中.同样的使 ...
- 北风设计模式课程---解释器模式(Interpreter Pattern)
北风设计模式课程---解释器模式(Interpreter Pattern) 一.总结 一句话总结: 不仅要通过视频学,还要看别的博客里面的介绍,搜讲解,搜作用,搜实例 设计模式都是对生活的抽象,比如用 ...
- 北风设计模式课程---外观模式(Facade)总结
北风设计模式课程---外观模式(Facade)总结 一.总结 一句话总结: 不仅要通过视频学,还要看别的博客里面的介绍,搜讲解,搜作用,搜实例 设计模式都是对生活的抽象,比如用户获得装备,我可以先装备 ...
- 北风设计模式课程---开放封闭原则(Open Closed Principle)
北风设计模式课程---开放封闭原则(Open Closed Principle) 一.总结 一句话总结: 抽象是开放封闭原则的关键. 1."所有的成员变量都应该设置为私有(Private)& ...
- 北风设计模式课程---接口分离原则(Interface Segregation Principle)
北风设计模式课程---接口分离原则(Interface Segregation Principle) 一.总结 一句话总结: 接口分离原则描述为 "客户类不应被强迫依赖那些它们不需要的接口& ...
随机推荐
- MyBatis框架 课程笔记
MyBatis框架 课程笔记 第1章 MyBatis简介 1.1 MyBatis历史 1)MyBatis是Apache的一个开源项目iBatis, 2010年6月这个项目由Apache Softw ...
- [BZOJ 3771] Triple(FFT+容斥原理+生成函数)
[BZOJ 3771] Triple(FFT+生成函数) 题面 给出 n个物品,价值为别为\(w_i\)且各不相同,现在可以取1个.2个或3个,问每种价值和有几种情况? 分析 这种计数问题容易想到生成 ...
- 使用ajax发送文件的三种方式及预览图片的方法,上传按钮美化
后端代码 def upload(request): if request.method == "GET": return render(request,'upload.html') ...
- jQuery之样式的类操作
方法:添加类addClass .删除类removeClass. 切换类toggleClass <style> div { width: 150px; height: 150px; b ...
- JDK集合框架源码分析 - 简单概要
1.类继承体系 在集合框架的类继承体系中,最顶层有两个接口Collection.Map: Collection 表示一组纯数据 Map 表示一组key-value对 Collection的类继承体系: ...
- 【学习总结】快速上手Linux玩转典型应用-第5章-远程连接SSH专题
课程目录链接 快速上手Linux玩转典型应用-目录 目录 1. 认识SSH 2. 服务器安装SSH服务 3. 客户端安装SSH工具 4. 客户端链接SSH服务 5. SSH config 6. SSH ...
- 43. Multiply Strings (JAVA)
Given two non-negative integers num1 and num2represented as strings, return the product of num1 and ...
- Mysql逻辑架构介绍
总体概览: 和其它数据库相比,MySQL有点与众不同,它的架构可以在多种不同场景中应用并发挥良好作用.主要体现在存储引擎的架构上,插件式的存储引擎架构将查询处理和其它的系统任务以及数据的存储提取相分离 ...
- ID学习一 Basic
Assignment 作用:定义变量并赋值 变量可以是新定义的也可以是已经存在的: 值可以是另一个变量的值.一个文本值.一个复杂的表达式(利用表达式编辑助手构造): 注意:一旦变量被定义,你不能删除变 ...
- qt02 textEdit
1.向QTextEdit中当前光标位置添加一行字符串message ui.messageTextEdit->textCursor().insertText(message+"\n&qu ...