Fork me on Gitee

Visitor模式(访问者设计模式)

Visitor ?

  • 在Visitor模式中,数据结构与处理被分离开来。我们编写一个表示“访问者”的类来访问数据结构中的元素,
    并把对各元素的处理交给访问者类。这样,当需要增加新的处理时,我们只需要编写新的访问者,然后让
    数据结构可以接受访问者的访问即可。 **

  • 概括: 数据结构与处理彼此分开,当需要实现新数据访问方式的时候,实现Visitor就行了,(缺点:如果增加元素的访问那会非常麻烦)

理清职责

  • 作用:这里用到Composition设计模式的那个文件和文件夹的例子作为访问者要访问的数据结构。
    地址:https://www.cnblogs.com/dgwblog/p/9840291.html
    名字================>>>> 说明
    | Visitor || 表示访问者的抽象类,它访问文件和文件夹
    |ELement || 表示数据结构的接口,它接受访问者的访问
    |ListVisitor|visitor || 类的子类,显示文件和文件夹一览
    |Fi1e类和Directory || 类的父类,它是抽象类
    |Entry(实现了Element接口)
    |File || 表示文件的类
    |Directory || 表示文件夹的类
    |FileTreatementException || 表示向文件中add时发生的异常的类
    |Main ||测试程序行为的类

  • 简单说明:

    Visitor与ELement 作用在你把代码阅读以后会发现真的是非常简单:如果说Composite中容器与内容一致性,这里是还是将内容一致性维持着,但是将
    那我们需要的数据结构的那部门的实现一套接口访问者提取出来,实现的真正的访问。


rootdir.acctep(new ListVisitor());
/*ListVisitor visitor = new ListVisitor();
visitor.visit(rootdir);*/


accept(接受)方法的调用方式如下。
element.accept(visitor);
而visit(访问)方法的调用方式如下。
visitor.visit (element);
把上面这两者情况,我们叫做消息的双重分发

UML

时序图:

  • 对于Directory类的实例和Fi1e类的实例,我们调用了它们的accept方法
  • 对于每一个Directory类的实例和File类的实例,我们只调用了一次它们的accept方法
  • 对于ListvVisitor的实例,我们调用了它的visit(Directory)和visit(File)方法
  • 处理visit(Directory)和visit(File)的是同一个ListVisitor的实例

Code

  • Entry :
public abstract class Entry implements Element {

    // 这里实现的Element的目的 是便于在后面的Concreate类中Visitor进行访问

    /**
     * 1. 文件名
     * 2. 文件大小
     * @return
     */
    public abstract String getName();
    public abstract int getSize();

    /**
     * Directory  增加条目
     * File 不能增加条目
     */
    public Entry add(Entry entry)throws FileTreatementException {
        throw new FileTreatementException();
    }

    /**
     * 增加数据的遍历方法itorator
     * @return
     */
    public Iterator iterator() throws FileTreatementException{
        throw new FileTreatementException();
    }

    @Override
    public String toString() {
        return getName()+"("+getSize()+")";
    }
}


  • Element、ListVisitor 、Visitor
public abstract class Visitor {

    /**
     * 作用: 这里的方法的重载数量决定你数据结构中数据的参数
     * 这里我们需要访问 File Directory
     */
    abstract void visit(File file);

    abstract void visit(Directory directory);

}

public interface Element {

    void acctep(Visitor visitor);
}
public class ListVisitor extends Visitor {

    private String currentDir="";

    @Override
    void visit(File file) {
        System.out.println(currentDir+"/"+file);
    }

    /**
     * 实现递归访问结构
     */
    @Override
    void visit(Directory directory) {
        System.out.println(currentDir+"/"+directory);
        String saveDir=currentDir;
        currentDir=currentDir+"/"+directory.getName();
        try {
            Iterator it = directory.iterator();
            while(it.hasNext()){
                Entry o = (Entry) it.next();
                o.acctep(this);
            }
            currentDir =saveDir;
        } catch (FileTreatementException e) {
            e.printStackTrace();
        }
    }
}

  • Directory,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 acctep(Visitor visitor) {
        visitor.visit(this);
    }
}

public class Directory extends Entry {

    private String name;

    private List<Entry> directory=new ArrayList<>();

    public Directory(String name) {
        this.name = name;
    }

    @Override
    public Entry add(Entry entry) throws FileTreatementException {
        directory.add(entry);
        return this;
    }

    @Override
    public String getName() {
        return name;
    }

    /**
     * getSize() | printList(String prefix)
     *
     * 都会递归去遍历下面可能存在的 目录或者文件的子项
     */

    @Override
    public int getSize() {
        int size=0;
        Iterator<Entry> it = directory.iterator();
        while (it.hasNext()){
            // 这里的Entry 可能是目录 也可能是文件
            Entry next = it.next();
            size+=next.getSize();
        }
        return size;
    }

    @Override
    public Iterator iterator() throws FileTreatementException {
        return directory.iterator();
    }

    @Override
    public void acctep(Visitor visitor) {
        visitor.visit(this);
    }
}


  • FileTreatementException ,
public class FileTreatementException extends Exception {

    public FileTreatementException() {
    }

    public FileTreatementException(String message) {
        super(message);
    }
}


  • MainT
public class MainT {

    public static void main(String[] args) throws FileTreatementException{
        System.out.println("start +++++++++++");

        Directory rootdir=new Directory("root");

        Directory bindir = new Directory("bin");
        Directory tempdir = new Directory("temp");
        Directory userdir = new Directory("user");

        rootdir.add(bindir);
        rootdir.add(tempdir);
        rootdir.add(userdir);

        bindir.add(new File("vi",1000));
        bindir.add(new File("notepaid",15000));

        rootdir.acctep(new ListVisitor());
        /*ListVisitor visitor = new ListVisitor();
        visitor.visit(rootdir);*/

    }
}

posted @ 2018-10-27 17:23  ---dgw博客  阅读(453)  评论(0编辑  收藏  举报