Visitor 模式
在Visitor 模式中,数据结构和处理被分离开。我们编写一个表示“访问者”的类来访问数据结构中的元素,并把对各元素的处理交给访问者类。这样,当需要增加新的处理时,我们只需要编写新的访问者,然后让数据结构可以接受访问者的访问即可。
示例程序
示例程序类图
Visitor
public abstract class Visitor {
public abstract void visit(File file);
public abstract void visit(Directory directory);
}
Element
public interface Element {
void accept(Visitor v);
}
Entry
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 iterator() throws FileTreatmentException { // 生成Iterator
throw new FileTreatmentException();
}
@Override
public String toString() { // 显示字符串
return getName() + " (" + getSize() + ")";
}
}
File
public class File extends Entry {
private String name;
private int size;
public File(String name, int size) {
this.name = name;
this.size = size;
}
@Override
public String getName() {
return name;
}
@Override
public int getSize() {
return size;
}
@Override
public void accept(Visitor v) {
v.visit(this);
}
}
Directory
import java.util.Iterator;
import java.util.ArrayList;
public class Directory extends Entry {
private String name; // 文件夹名字
private ArrayList dir = new ArrayList(); // 目录条目集合
public Directory(String name) { // 构造函数
this.name = name;
}
@Override
public String getName() { // 获取名字
return name;
}
@Override
public int getSize() { // 获取大小
int size = 0;
Iterator it = dir.iterator();
while (it.hasNext()) {
Entry entry = (Entry)it.next();
size += entry.getSize();
}
return size;
}
@Override
public Entry add(Entry entry) { // 增加目录条目
dir.add(entry);
return this;
}
@Override
public Iterator iterator() { // 生成Iterator
return dir.iterator();
}
@Override
public void accept(Visitor v) { // 接受访问者的访问
v.visit(this);
}
}
ListVisitor
import java.util.Iterator;
public class ListVisitor extends Visitor {
private String currentdir = ""; // 当前访问的文件夹的名字
@Override
public void visit(File file) { // 在访问文件时被调用
System.out.println(currentdir + "/" + file);
}
@Override
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;
}
}
FileTreatmentException
public class FileTreatmentException extends RuntimeException {
public FileTreatmentException() {
}
public FileTreatmentException(String msg) {
super(msg);
}
}
Main
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();
}
}
}
Visitor模式中登场的角色
1.Visitor(访问者)
Visitor角色负责对数据结构中每个具体的元素(ConcreteElement角色)声明一个用于访问xxxxx的visit(xxxxx)方法。visit(xxxxx)是用于处理xxxxx的方法,负责实现该方法的是ConcreteVisitor角色。在示例程序中,由Visitor类扮演此角色。
2.ConcreteVisitor(具体的访问者)
ConcreteVisitor角色负责实现Visitor角色所定义的接口(API)。它要实现所有的visit(xxxxx)方法,即实现如何处理每个ConcreteElement角色。在示例程序中,由ListVisitor类扮演此角色。如同在ListVisitor中,currentdir字段的值不断发生变化一样,随着visit(xxxxx)处理的进行,ConcreteVisitor角色的内部状态也会不断地发生变化。
3.Element(元素)
Element角色表示Visitor角色的访问对象。它声明了接受访问者的accept方法。accept方法接收到的参数是Visitor角色。在示例程序中,由Element接口扮演此角色。
4.ConcreteElement
ConcreteElement角色负责实现Element角色所定义的接口(API)。在示例程序中,由File类和Directory类扮演此角色。
5.ObjectStructure(对象结构)
ObjectStructure角色负责处理Element角色的集合。ConcreteVisitor角色为每个Element角色都准备了处理方法。在示例程序中,由Directory类扮演此角色(一人分饰两角)。为了让ConcreteVisitor角色可以遍历处理每个Element角色,在示例程序中,我们在Directory类中实现了iterator方法。