一、前言

什么叫访问,如果大家学过数据结构,对于这点就很清晰了,遍历就是访问的一般形式,单独读取一个元素进行相应的处理也叫作访问,读取到想要查看的内容+对其进行处理就叫作访问,那么我们平常是怎么访问的呢,基本上就是直接拿着需要访问的地址来读写内存就可以了。

为什么还要有一个访问者模式呢,这就要放到OOP之中了,在面向对象编程的思想中,我们使用类来组织属性,以及对属性的操作,那么我们理所当然的将访问操作放到了类的内部,这样看起来没问题,但是我们想要使用另一种遍历方式要怎么办呢,我们必须将这个类进行修改,这在设计模式中是大忌,在设计模式中就要保证,对扩展开发,对修改关闭的开闭原则。

因此,我们思考,可不可以将访问操作独立出来变成一个新的类,当我们需要增加访问操作的时候,直接增加新的类,原来的代码不需要任何的改变,如果可以这样做,那么我们的程序就是好的程序,因此可以扩展,符合开闭原则。而访问者模式就是实现这个的,使得使用不同的访问方式都可以对某些元素进行访问。

二、代码

package designMode.visitor;import sun.reflect.generics.visitor.Visitor;public interface Element {public abstract void accept(Visitor visitor);
}
package designMode.visitor;import designMode.iterator.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();}@Overridepublic String toString() {return getName()+"<"+getSize()+">";}
}
package designMode.visitor;import sun.reflect.generics.visitor.Visitor;public class File extends Entry{private String name;private int size;public File(String name, int size) {this.name = name;this.size = size;}@Overridepublic String getName() {return name;}@Overridepublic int getSize() {return size;}@Overridepublic void printList(String prefix) {System.out.println(prefix+"/"+this);}public void accept(Visitor visitor){visitor.visit(this);}
}
package designMode.visitor;import sun.reflect.generics.visitor.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;}@Overridepublic String getName() {return name;}@Overridepublic int getSize() {int size = 0;Iterator it = entrys.iterator();while (it.hasNext()){size += ((Entry)it.next()).getSize();}return size;}@Overridepublic Entry add(Entry entry){entrys.add(entry);return this;}public Iterator iterator(){return entrys.iterator();}@Overridepublic 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){visitor.visit(this);}
}
package designMode.visitor;public abstract class Visitor {public abstract void visit(File file);public abstract void visit(Directory directory);
}
package designMode.visitor;import java.util.Iterator;public class ListVisitor extends Visitor {String currentDir = "";@Overridepublic void visit(File file) {System.out.println(currentDir+"/"+file);}@Overridepublic 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;}
}
package designMode.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();}}
package designMode.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中的结果输出出来,我们当然也可以直接在遍历的时候就输出出来,这个看我们的使用要求了。因此可以看到在保证数据结构不发生变化的情况下,可以非常方便增加新的一种访问方法,只需要新增加一个访问类即可,但是如果我们数据结构发生变化之后,就需要修改继承自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);}}

上面的代码是在组合模式类的内部遍历的过程,可以明确的看到递归(直接递归)的使用。我们看一下访问者模式中的间接递归:

public void accept(Visitor visitor) {//System.out.println("开始访问文件夹:"+this);visitor.visit(this);//System.out.println("结束访问文件夹:"+this);//System.out.println();}
public void accept(Visitor visitor) {//System.out.println("开始访问文件:"+this);visitor.visit(this);//System.out.println("结束访问文件:"+this);//System.out.println();}
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;}

我们看到了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 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访问者模式相关推荐

  1. [导入]C#面向对象设计模式纵横谈(24):(行为型模式) Visitor 访问者模式.zip(10.41 MB)...

    讲座内容: 本培训课程探讨GoF23之 Visitor 访问者模式的意图.动因.原理.应用场景与C#语言实现,以及该模式在.NET框架程序设计中的具体应用. 课程讲师: 李建忠 上海祝成信息科技有限公 ...

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

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

  3. 浅谈示波器X-Y模式 示波器触发模式及使用

    描述 示波器是一种用途十分广泛的电子测量仪器.它能把肉眼看不见的电信号变换成看得见的图像,便于人们研究各种电现象的变化过程.接下来我们就来了解一下示波器的X-Y模式以及示波器触发模式,同时了解一下两种 ...

  4. 建造者模式浅谈 与工厂模式的区别

    感谢您的阅读.如果感觉文章对您有用,麻烦您动动手指点个赞,以资鼓励.谢谢! 转载请注明出处哈 建造者模式浅谈 与工厂模式的区别_茄子_土豆的博客-CSDN博客_建造者模式和工厂模式的区别 创建对象时构 ...

  5. 设计模式——Visitor(访问者)模式

    目录 前言 1 定义 2 适用性 3 结构 3.1 结构图 3.2 参与者 4 Java实际应用举例--以ASM技术为例 4.1 被访问对象--ClassReader 4.2 Visitor--Cla ...

  6. 访问者模式 php,php设计模式 Visitor 访问者模式

    /** * 访问者模式 * * 表示一个作用于某对象结构中的各元素的操作,可以在不改变各元素的类的前提下定义作用于这些元素的新操作 * */ abstract class Visitor { abst ...

  7. 浅谈Interpreter解释器模式

    一.前言 这是我们23个设计模式中最后一个设计模式了,大家或许也没想到吧,竟然是编译原理上的编译器,这样说可能不对,因为编译器分为几个部分组成呢,比如词法分析器.语法分析器.语义分析器.中间代码优化器 ...

  8. 设计模式之略见一斑(Visitor访问者模式)

    在开发中,我们可能会经常碰到客户提了一新的需求,那么在现有的类实现新的需求呢?通常的做法是添加新的方法.但是有时候我们只能看到接口,而根本无法看到其接口实现.这个时候我们就无法往接口里添加接的方法.但 ...

  9. java visitor_Java设计模式(三) Visitor(访问者)模式及多分派场景应用

    基本概念 Visitor 封装一些作用于数据结构中的各元素的操作,不同的操作可以借助新的visitor实现,降低了操作间的耦合性 访问者可以将数据结构和对数据的操作解耦,使得增加对数据结构的操作不需要 ...

最新文章

  1. 2021年大数据Spark(九):Spark On Yarn两种模式总结
  2. Java JDK 学习笔记:File类
  3. __len__的作用
  4. 主线程 唤醒_Java等待唤醒机制统计子线程运行时间的方式及其疑问
  5. 克里斯蒂安贝尔_克里斯蒂安贝尔和乌云背后的幸福线导演合作神秘“大片”
  6. vuex第三弹vuex之actions(前端网备份)
  7. .NET系列 之 开源和不开源的背后
  8. 爬取网页的通用代码框架
  9. nvidia显卡对比分析
  10. 计算机网络符号显示叹号,在Win7系统中,电脑网络出现感叹号怎么解决?
  11. PhpStrom 配置Xdebug
  12. bs模式Java web,基于BS模式的即时通讯系统的设计与实现(MyEclipse)
  13. AndroidDeveloper Weekly No.5
  14. opencv检测相交点_OpenCV学习18--霍夫变换检测直线
  15. Spring Cloud Alibaba @SentinelResource配置详解
  16. 零基础快速入行入职软件测试工程师
  17. 永远的《毕业生》之《Scarborough Fair》
  18. Leetcode刷题114. 二叉树展开为链表
  19. css hack方法,css hack方式有哪些
  20. 努努小说通用爬取——多线程

热门文章

  1. 如何把iPAD投屏到MacBook上(不用安装任何东西)
  2. nao机器人拆解_华为P7拆解:超薄机身,4G之下续航略显不足
  3. 专家解惑瘦子吃不胖秘密
  4. pip install airsim问题
  5. 使用TexturePacker打包游戏贴图制作帧动画
  6. go gin下配置https
  7. linux 扫描局域网内所有主机
  8. 丑萌字,丑萌丑萌的好可爱。
  9. matlab动态文字,matlab GUI界面文字动态显示
  10. 80台无盘服务器配置,80台无盘服务器配置