Visitor模式可以用来把数据结构与处理分离开。通俗来说就是编写一个访问者类来访问数据结构中的元素,并把对各元素的处理交给访问者类。这样,当需要增加新的处理时,只需要编写新的访问者,然后让数据结构可以接受访问者的访问即可。

  下面先看示例程序的类图。

  在示例程序中,使用Composite模式中用到了那个文件和文件夹的例子作为访问者要访问的数据结构。访问者会访问由文件和文件夹构成的数据结构,然后显示出文件和文件夹的一览。

 package bigjunoba.bjtu.visitor;

 public abstract class Visitor {
public abstract void visit(File file);
public abstract void visit(Directory directory);
}

  Visitor类是表示访问者的抽象类。访问者依赖于它所访问的数据结构(即File类和Directory类)。这里visit方法用到了重载,分别表示了用于访问文件和文件夹的方法。

 package bigjunoba.bjtu.visitor;

 public interface Element {
public abstract void accept(Visitor v);
}

  Element接口是接受访问者的访问的接口。accept方法的参数是访问者Visitor类。

 package bigjunoba.bjtu.visitor;

 import java.util.Iterator;

 public abstract class Entry implements Element {
public abstract String getName(); // 获取名字
public abstract int getSize(); // 获取大小
public Entry add(Entry entry) throws FileTreatmentException { // 增加目录条目
throw new FileTreatmentException();
}
public Iterator<Element> iterator() throws FileTreatmentException { // 生成Iterator
throw new FileTreatmentException();
}
public String toString() { // 显示字符串
return getName() + " (" + getSize() + ")";
}
}

  Entry类与Composite模式中的Entry类是一样的,不过该Entry类实现了Element接口,这是为了让Entry类适用于Visitor模式。实际上实现Element接口中声明的抽象方法accept的是Entry类的子类。

  add方法仅对Directory类有效,因此在Entry类中,让它简单地报错。同样,用于获取Iterator的iterator方法也仅对Directory类有效,也让它简单地报错。

 package bigjunoba.bjtu.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 accept(Visitor v) {
v.visit(this);
}
}

  File类中要注意的是它是如何实现accept接口的。accept方法的参数是Visitor类,然后调用Visitor类的visit方法,由于这里的this是File类的实例,所以调用的是第一个visit方法。

 package bigjunoba.bjtu.visitor;

 import java.util.Iterator;
import java.util.ArrayList; public class Directory extends Entry {
private String name; // 文件夹名字
private ArrayList<Entry> dir = new ArrayList<Entry>(); // 目录条目集合
public Directory(String name) { // 构造函数
this.name = name;
}
public String getName() { // 获取名字
return name;
}
public int getSize() { // 获取大小
int size = 0;
Iterator<Entry> it = dir.iterator();
while (it.hasNext()) {
Entry entry = (Entry)it.next();
size += entry.getSize();
}
return size;
}
public Entry add(Entry entry) { // 增加目录条目
dir.add(entry);
return this;
}
public Iterator iterator() { // 生成Iterator
return dir.iterator();
}
public void accept(Visitor v) { // 接受访问者的访问
v.visit(this);
}
}

  同File类一样,调用visit的第一个方法,告诉访问者“当前正在访问的是Directory类的实例”。

 package bigjunoba.bjtu.visitor;

 import java.util.Iterator;

 public class ListVisitor extends Visitor {
private 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 = currentdir + "/" + directory.getName();
Iterator it = directory.iterator();
while (it.hasNext()) {
Entry entry = (Entry)it.next();
entry.accept(this);
}
currentdir = savedir;
}
}

  ListVisitor类是访问数据结构并显示元素一览。visit(Directory directory)方法,遍历文件夹中的所有目录条目并调用它们各自的accept方法,然后accept方法调用visit方法,visit方法又会调用accept方法,这样就形成了非常复杂的递归调用。通常的递归调用是某个方法调用自身,在Visitor模式中,则是accept方法与visit方法之间相互调用。

 package bigjunoba.bjtu.visitor;

 public class FileTreatmentException extends RuntimeException {
public FileTreatmentException() {
}
public FileTreatmentException(String msg) {
super(msg);
}
}

  处理异常类。

 package bigjunoba.bjtu.visitor;

 public class Main {
public static void main(String[] args) {
try {
System.out.println("Making root entries...");
Directory rootdir = new Directory("root");
Directory bindir = new Directory("bin");
Directory tmpdir = new Directory("tmp");
Directory usrdir = new Directory("usr");
rootdir.add(bindir);
rootdir.add(tmpdir);
rootdir.add(usrdir);
bindir.add(new File("vi", 10000));
bindir.add(new File("latex", 20000));
rootdir.accept(new ListVisitor()); System.out.println("");
System.out.println("Making user entries...");
Directory yuki = new Directory("yuki");
Directory hanako = new Directory("hanako");
Directory tomura = new Directory("tomura");
usrdir.add(yuki);
usrdir.add(hanako);
usrdir.add(tomura);
yuki.add(new File("diary.html", 100));
yuki.add(new File("Composite.java", 200));
hanako.add(new File("memo.tex", 300));
tomura.add(new File("game.doc", 400));
tomura.add(new File("junk.mail", 500));
rootdir.accept(new ListVisitor());
} catch (FileTreatmentException e) {
e.printStackTrace();
}
}
}

  这里和Composite模式做比较,Composite模式中调用printList方法来显示文件夹中的内容,该方法已经在Directory类(即表示数据结构的类)中实现了。而在Visitor模式中是在访问者中显示文件夹中的内容。这是因为显示文件夹中的内容业数据对数据结构中的各元素进行的处理。

Making root entries...
/root (30000)
/root/bin (30000)
/root/bin/vi (10000)
/root/bin/latex (20000)
/root/tmp (0)
/root/usr (0) Making user entries...
/root (31500)
/root/bin (30000)
/root/bin/vi (10000)
/root/bin/latex (20000)
/root/tmp (0)
/root/usr (1500)
/root/usr/yuki (300)
/root/usr/yuki/diary.html (100)
/root/usr/yuki/Composite.java (200)
/root/usr/hanako (300)
/root/usr/hanako/memo.tex (300)
/root/usr/tomura (900)
/root/usr/tomura/game.doc (400)
/root/usr/tomura/junk.mail (500)

  运行结构和Composite模式中的运行结果一样。

最新文章

  1. docker——容器安装tomcat
  2. .Net深入实战系列—JSON序列化那点事儿
  3. js自动提示查询添加功能(不是自动补全)
  4. 关于Linux与Windows的区别的文章
  5. 【BZOJ】1998: [Hnoi2010]Fsk物品调度
  6. SharePoint 2013 图像呈现形式介绍
  7. NP完全问题 NP-Completeness
  8. css样式自适应,支持数字
  9. vijosP1903学姐的实习工资
  10. window FILES——windows文件管理相关实例
  11. UNIX环境下的共享内存
  12. 静态化 - 伪静态技术(Apache Rewrite 实现)
  13. ios开发问题:添加库和复制其它工程文件编译错误问题
  14. 报错 &#39;dict&#39; object has no attribute &#39;has_key&#39;
  15. Linux如何查看机器的配置信息
  16. java使用httpclient封装post请求和get的请求
  17. Spark远程调试参数
  18. Spring Cloud系列之Feign的常见问题总结
  19. 关于查询报表总是&quot;超时已过期&quot;的问题解决
  20. Machine Learning Trick of the Day (2): Gaussian Integral Trick

热门文章

  1. Python+OpenCV竖版古籍文字分割
  2. 【Django】中间件,csrf,缓存,信号
  3. JsonMessageView工具类
  4. 获取Android设备标识符
  5. JavaScript实现各种排序算法
  6. SpringBootSecurity学习(20)前后端分离版之OAuth2.0刷新token
  7. Spring Cloud Config Server 节点迁移引起的问题,请格外注意这一点!
  8. .Net Core下使用HtmlAgilityPack解析采集互联网数据
  9. 分享一个移动端rem布局的适配mixin
  10. 项目二:企业级java电商网站开发(服务端)