1、引入
在Composite模式中,我们访问一个文件夹目录,是通过文件夹与文件自带的输出方法实现的。这样的方式在需要拓展功能时较为不易:
需要修改或添加各个文件(文件夹)的输出方法,这带来了较大的工程量并且容易出错。
而visitor模式可以解决中这个问题--通过访问者自己提供的文件输出方法代替文件自带的输出方法,将数据结构与处理分离开,达到了解耦的目的。
2、示例
2.1、Vistor抽象父类
public abstract class Visitor {
public abstract void visit(File file);
public abstract void visit(Directory directory);
}
2.2、文件
2.2.1、Element接口
在此接口内,定义了接受访问者访问的抽象方法:
public interface Element {
public abstract void accept(Visitor v);
}
2.2.2、Entry类
作为所有文件的抽象父类:
public abstract class Entry implements Element{
public abstract String getName();
public abstract int getSize();
public Composite.example.Entry add(Composite.example.Entry entry)throws FileTreatMentException {
throw new FileTreatMentException();
}
public String toString() {
return getName()+"("+getSize()+")";
}
}
其中FileTreatMentException类为:
public class FileTreatMentException extends RuntimeException{
public FileTreatMentException() {
}
public FileTreatMentException(String msg) {
super(msg);
}
}
2.2.3、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 void accept(Visitor v) {
v.visit(this);
}
@Override
public String getName() {
return name;
}
@Override
public int getSize() {
return size;
}
}
2.2.4、Directory类
定义了具体的文件夹:
public class Directory extends Entry{
private String name;
private ArrayList<Entry> dir=new ArrayList<>();
public Directory(String name) {
this.name = name;
}
@Override
public void accept(Visitor v) {
v.visit(this);
}
@Override
public String getName() {
return name;
}
@Override
public int getSize() {
int size=0;
Iterator<Entry> iterator = dir.iterator();
while (iterator.hasNext()) {
Entry next = iterator.next();
size+=next.getSize();
}
return size;
}
public Iterator iterator() {
return dir.iterator();
}
public Entry add(Entry entry) {
dir.add(entry);
return this;
}
}
2.3、Visitor实现之ListVisitor
提供了通过列表形式展现文件夹与文件的方法:
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 savedDir=currentDir;
currentDir=currentDir+"/"+directory.getName();
Iterator iterator = directory.iterator();
while (iterator.hasNext()) {
Entry next = (Entry)iterator.next();
next.accept(this);
}
currentDir=savedDir;
}
}
2.4、测试
public class Main {
public static void main(String[] args) {
System.out.println("Making entries....");
Directory root = new Directory("root");
Directory bin = new Directory("bin");
Directory tmp = new Directory("tmp");
root.add(bin);
root.add(tmp);
bin.add(new File("vim", 1000));
bin.add(new File("ls", 500));
tmp.add(new File("mysql_log", 20000));
tmp.add(new File("system_security_log)",100000));
root.accept(new ListVisitor());
}
}
运行结果:
3、tips
- 虽然从运行结果来看,Vistor模式与Composite模式的结果相同,但当要提供新的输出方式时,前者只需要再写一个Vistor子类,在其中重载visit方法即可。
- 从可拔插的角度上来看,Visitor模式与Strategy模式较为相像,不过前者注重在方法使用上的插件,后者注重在对象创建时提供插件。