访问者模式(Visitor Pattern)

访问数据结构并处理数据

数据结构中保存着许多的元素,在对这些元素处理的时候,将处理代码放入数据结构中,当每次增加或者修改处理方法时,都必须将数据结构的类进行修改,因此,我们需要用到访问者模式,数据结构和数据处理被分离出来,创建一个访问者专门来访问数据结构中的元素,把元素的处理交给访问者类,这样增加或修改时不需要改动数据结构。


1.实例

使用访问者模式来访问文件夹模式的数据结构

Vistitor类

访问者的抽象类,定义访问的方法,规定一种数据处理的模式.

/**
 * @author Jay
 * @date 2019/7/6 21:13
 * @description
 */
public abstract class Visitor {
    public abstract void visit(File file);
    public abstract void visit(Directory directory);
}

Element接口

接收访问者的接口,访问者通过这个接口去访问数据结构,其中带有访问者类型参数,将参数传入。

/**
 * @author Jay
 * @date 2019/7/6 21:14
 * @description 接口接收访问者
 */
public interface Element {
    void accept(Visitor v);
}

Entry类

是一个制定规则的抽象类,定义了共有的方法.并且实现接口,给访问者提供入口。


/**
 * @author Jay
 * @date 2019/7/6 21:15
 * @description
 */
public abstract class Entry {
    public abstract String getName();

    public abstract int getSize();

    /**
     * 子类去调用此方法时会抛出异常,父类也可以不规定此方法,文件夹类独有方法,又构成组合关系.
     */
    public Entry add(Entry entry) throws FileTreatmentException {
        throw new FileTreatmentException();

    }

    public void printList() { //重载方法,这样可以区别出第一次,也可以不必在父类中写,子类中自己判断输出结构.
        // 显示条目
        printList("");
    }

    protected abstract void printList(String prefix); // 为一览加前缀

    protected abstract void accept(Visitor v);

    @Override
    public String toString() {

        return getName() + "(" + getSize() + ")";
    }
}

File类

/**
 * @author Jay
 * @date 2019/7/6 21:16
 * @description
 */
public class File extends Entry {
    private String name;
    private int size;
    public File(String name, int size) {
        super();
        this.name = name;
        this.size = size;
    }
    @Override
    public String getName() {
        return name;
    }
    @Override
    public int getSize() {

        return size;
    }
    @Override
    protected void printList(String prefix) {
        System.out.println(prefix + "/" + this);
    }
    @Override
    public void accept(Visitor v) {
        v.visit(this);

    }
}

Directory类

/**
 * @author Jay
 * @date 2019/7/6 21:17
 * @description
 */
public class Directory extends Entry {

    private String name;
    // 只有文件夹才是数组结构,文件只是其中的元素,所以类型为directory
    // 而其中元素可以是文件,也可以是文件夹,所以泛型为父类泛型
    // 多个数组的套嵌结构,形成了文件树的结构形式.
    private ArrayList<Entry> directory = new ArrayList<Entry>();

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

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

    @Override
    public int getSize() { // 获取大小
        int size = 0;
        Iterator it = directory.iterator();

        while (it.hasNext()) {

            Entry entry = (Entry) it.next();
            // 获取长度
            size += entry.getSize(); 
        }
        return size;
    }

    /**
     * 重写了父类的添加方法.
     */
    @Override
    public Entry add(Entry entry) {
        directory.add(entry);
        return this;
    }

    @Override
    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);
        }
    }

    public Iterator iterator() {
        return directory.iterator();
    }

    @Override
    public void accept(Visitor v) {
        v.visit(this);
    }
}

ListVisitor类

/**
 * @author Jay
 * @date 2019/7/6 21:18
 * @description
 */
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;
    }
}

测试类

public class Main {
    public static void main(String[] args) {
        Directory dirRoot = new Directory("root");
        Directory dirUser = new Directory("user");
        Directory dirBin = new Directory("bin");
        Directory dirTmp = new Directory("tmp");

        dirRoot.add(dirBin);
        dirRoot.add(dirUser);
        dirRoot.add(dirTmp);
        dirBin.add(new File("bin1", 1024));

        Directory dirAdmin = new Directory("admin");
        dirUser.add(dirAdmin);
        dirUser.add(new File("admin1", 1024));
        dirRoot.printList();
        System.out.println("+========================+");
        dirRoot.accept(new ListVisitor());
    }
}
/root(2048)
/root/bin(1024)
/root/bin/bin1(1024)
/root/user(1024)
/root/user/admin(0)
/root/user/admin1(1024)
/root/tmp(0)
+========================+
/root(2048)
/root/bin(1024)
/root/bin/bin1(1024)
/root/user(1024)
/root/user/admin(0)
/root/user/admin1(1024)
/root/tmp(0)                  定义了相同的处理规则,所以返回的数据处理结果相同.

模式将数据结构形式与数据处理方式分隔开,数据结构提供对外的接口将本实例传入访问者类中,具体的数据处理方式由访问者自己定义,从而实现了解耦。


2.模式中角色

Visitor(访问者)

负责对数据结构中每个具体的元素声明一个访问方法。

ConcreteVististor(具体的访问者)

负责实现接口中的方法,只要实现了接口,就可以有不同的实现类,从而可以很方便的增加和修改对数据结构的访问功能。

Element(元素)

表示Visitor中的访问对象,声明了如何接收访问,将数据直接传递给访问者去做处理。

ConcreteElement

数据结构的具体类,实现了元素接口。

ObjectStructure(对象结构)

负责处理Element角色的集合,这样不仅仅可以做到同一个数据结构可以有不同的处理方案,而且可以不同的数据结构共用同一个处理方法。

此模式将数据结构与对数据的处理分开,在上层以父类或者接口来进行指定规则。

开闭原则:

  • 对扩展开放.
  • 对修改关闭.

To GitHub

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值