根据数据结构访问数据-Visitor模式

115 阅读2分钟

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());
    }
}

运行结果:

image.png

3、tips

  • 虽然从运行结果来看,Vistor模式与Composite模式的结果相同,但当要提供新的输出方式时,前者只需要再写一个Vistor子类,在其中重载visit方法即可。
  • 从可拔插的角度上来看,Visitor模式与Strategy模式较为相像,不过前者注重在方法使用上的插件,后者注重在对象创建时提供插件。