图解设计模式 (十三) Visitor 模式

读书笔记 仅供参考

简述

通常将数据结构和处理数据的代码放到同一个类中,但是如果有多种处理方式,增加处理方式就会多次修改数据类。Visitor 模式(访问者模式)将数据结构与处理分离开来,增加新的处理时,只需要编写新的访问者就可以了。

UML 和角色

Visitor

负责对数据结构中每个具体的元素(ConcreteElement 角色)声明一个用于访问的 visit 方法。

ConcreteVisitor

负责实现 Visitor 角色定义的接口(API),要实现所有的 visit 方法。

Element

表示 Visitor 觉得访问对象,声明了接收访问者的 accept 方法。accept 方法的参数是 Visitor 角色。

ConcreteElement

负责实现 Element 角色定义的接口(API)。

ObjectStructure

负责处理 Element 角色的集合。

UML

 

例子

例程是一个访问文件夹和文件,显示文件名和大小的程序。ListVisitor 作为访问者,拥有两个方法,visit(File) 和 visit(Directory),将 ListVisitor 比作一个寻求帮助的人,那么 File 和 Directory 就是两个家庭(他们都属于家庭,虽然不一样)。当他们选择接收(accept 方法)访问者时,访问者就根据家庭的不同干不同的事(visit 方法)。

 UML

//表示访问者的抽象类,依赖于访问的数据结构
public abstract class Visitor {
    //根据不同的参数调用不同的访问方法
    public abstract void visit(File file);
    public abstract void visit(Directory directory);
}
//访问数据结构并显示数据一览
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<Entry> it = directory.iterator();
        while (it.hasNext()) {
            Entry entry = it.next();
            entry.accept(this);
        }
        currentDir = saveDir;
    }
}
public interface Element {
    void accept(Visitor visitor);
}
//相当于 Composite 模式的父类,在 Visitor 模式中使用并不是必须的
//真正作为被访问者的是它的子类
public abstract class Entry implements Element {
    public abstract String getName();

    public abstract int getSize();

    public Entry add(Entry entry) throws FileTreatmentException {
        throw new FileTreatmentException();
    }

    public String toString() {
        return getName() + " (" + getSize() + ")";
    }
}
// 作为被访问的元素
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
    //告诉 Visitor 正在访问的对象是 File 的实例
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}
public class Directory extends Entry {
    private String name;
    private List<Entry> dir = new ArrayList<>();

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

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

    @Override
    public int getSize() {
        int size = 0;
        Iterator<Entry> it = dir.iterator();
        while (it.hasNext()) {
            Entry entry = it.next();
            size += entry.getSize();
        }
        return size;
    }

    @Override
    public Entry add(Entry entry) throws FileTreatmentException {
        dir.add(entry);
        return this;
    }

    public Iterator<Entry> iterator() {
        return dir.iterator();
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}
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.accept(new ListVisitor());

            System.out.println("");
            System.out.println("Making user entries");
            Directory yuki = new Directory("yuki");
            Directory hanako = new Directory("hanako");
            Directory tomura = new Directory("tomura");
            usrDir.add(yuki);
            usrDir.add(hanako);
            usrDir.add(tomura);
            yuki.add(new File("diary.html", 100));
            yuki.add(new File("Composite.java", 200));
            hanako.add(new File("memo.tex", 300));
            tomura.add(new File("game.doc", 400));
            tomura.add(new File("junk.mail", 500));
            rootDir.accept(new ListVisitor());
        } catch (FileTreatmentException e) {
            e.printStackTrace();
        }
    }
}

运行结果

 这里写图片描述

 

 

要点

双重分发

像在 Visitor 模式中两个角色共同决定了实际进行的处理的消息分发的方式被称为双重分发。

开闭原则

Visitor 符合开闭原则:对扩展开发,对修改关闭。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值