访问者模式(访问数据结构并处理数据)
用处
将数据结构与处理分离开来。
角色
- Visitor(访问者)
该角色负责对数据结构中每个具体的元素e(ConcreteElement角色)声明一个用于访问e的visit(e)方法。visit(e)是用于处理e的方法,负责实现该方法的是ConcreteVisitor角色。 - ConcreteVisitor(具体的访问者)
该角色负责实现Visitor角色所定义的方法(API)。它要实现所有的visit(e)方法,即如何处理每个ConcreteElement角色。 - Element
该角色表示Visitor角色的访问对象。它声明了访问者的accept方法。accept方法接受到的参数是Visitor角色。 - ConcreteElement
该角色负责实现Visitor角色的访问对象,它声明了接受访问者的accept方法。accept方法接收到的参数是Visitor角色。 - ObjectStructure
该角色负责处理Element角色的集合。ConcreteVisitor角色为每个Element角色都准备了处理方法。
类图
这个类图其实没太看懂,个人认为Element和Visitor有个依赖关系。
- Visitor接口被ConcreteVisitor类实现并在visitor方法中处理数据结构
- Element接口中聚合了ObjectStructure类(需要访问的数据结构)
- ConcreteElement类接受Visitor对象作为参数,并调用Visitor对象的visit方法将自身this作为参数传入,实现双委派。
举例
//Client角色
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.printList("");
rootdir.accept(new ListVisitor());
System.out.println("");
System.out.println("Making user entries");
Directory yuki = new Directory("yuki");
Directory hanako = new Directory("hanako");
Directory tomaru = new Directory("tomaru");
usrdir.add(yuki);
usrdir.add(hanako);
usrdir.add(tomaru);
yuki.add(new File("diary.html",100));
yuki.add(new File("Composite.java",200));
hanako.add(new File("memo.tex",300));
tomaru.add(new File("game.doc",400));
tomaru.add(new File("junk.mail",500));
// rootdir.printList("");
rootdir.accept(new ListVisitor());
}catch(FileTreatmentException e){
e.printStackTrace();
}
}
}
//Visitor角色
abstract class Visitor{
public abstract void visit(File file);
public abstract void visit(Directory directory);
}
//ConcreteVisitor角色
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;
}
}
//Element角色
interface Element{
void accept(Visitor v);
}
class FileTreatmentException extends RuntimeException{
public FileTreatmentException(){
}
public FileTreatmentException(String msg){
super(msg);
}
}
//ConcreteElement角色
abstract class Entry implements Element{
public abstract String getName();
public abstract int getSize();
public Entry add(Entry entry) throws FileTreatmentException{
throw new FileTreatmentException();
}
// protected abstract void printList(String prefix);
public String toString(){
return getName() + " ("+getSize()+")";
}
public Iterator iterator() throws FileTreatmentException{
throw new FileTreatmentException();
}
}
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;
}
// protected void printList(String prefix){
// System.out.println(prefix + "/" +this);
// }
@Override
public void accept(Visitor v) {
v.visit(this); //双重委派
}
}
class Directory extends Entry{
private String name;
private ArrayList directory = new ArrayList();
public Directory(String name){
this.name = name;
}
public String getName(){
return name;
}
public int getSize(){
int size = 0;
Iterator it = directory.iterator();
while(it.hasNext()){
Entry entry = (Entry)it.next();
size += entry.getSize();
}
return size;
}
public Entry add(Entry entry){
directory.add(entry);
return this;
}
// protected void printList(String prefix){
// System.out.println(prefix +"/"+this);
// Iterator it = directory.iterator();
// while(it.hasNext()){
// Entry entry = (Entry)it.next();
// entry.printList(prefix + "/"+ name);
// }
// }
@Override
public Iterator iterator() throws FileTreatmentException {
return directory.iterator();
}
@Override
public void accept(Visitor v) {
v.visit(this);
}
}
由被注释掉的部分,我们可以看出对数据结构处理的工作独立出来,在新增的LIstVisitor中完成。
总结
- 符合开闭原则
- 将处理从数据结构中分离,提高扩展性。