设计模式学习笔记——访问者(Visitor)模式

设计模式学习笔记——访问者(Visitor)模式

@(设计模式)[设计模式, 访问者模式, visitor]

基本介绍

访问者模式主要的作用是对对象中元素进行操作。
使用访问者模式,在更改对元素的操作时,不需要更改类的定义。

访问者案例

类图

访问者案例类图

实现代码

Visitor抽象类
package com.pc.visitor.example;

/**
 * 访问者抽象类
 * Created by Switch on 2017-02-22.
 */
public abstract class Visitor {
    /**
     * 访问文件
     *
     * @param file 文件对象
     */
    public abstract void visit(File file);

    /**
     * 访问文件夹
     *
     * @param directory 文件夹对象
     */
    public abstract void visit(Directory directory);
}
ListVisitor类
package com.pc.visitor.example;

import java.util.Iterator;

/**
 * 列表访问类
 * Created by Switch on 2017-02-22.
 */
public class ListVisitor extends Visitor {
    /**
     * 当前访问的文件夹的名字
     */
    private String currentDir = "";

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

    @Override
    public void visit(Directory directory) {
        System.out.println(currentDir + "/" + directory);
        // 递归调用,回溯法
        String saveDir = this.currentDir;
        this.currentDir = this.currentDir + "/" + directory.getName();
        Iterator<Entry> iterator = directory.iterator();
        while (iterator.hasNext()) {
            Entry entry = iterator.next();
            entry.accept(this);
        }
        this.currentDir = saveDir;
    }
}
Element接口
package com.pc.visitor.example;

/**
 * 元素接口(代表可以接受访问)
 * Created by Switch on 2017-02-22.
 */
public interface Element {
    /**
     * 接受访问者对象访问
     *
     * @param visitor 访问者对象
     */
    void accept(Visitor visitor);
}
Entry抽象类
package com.pc.visitor.example;

/**
 * 条目抽象类
 * Created by Switch on 2017-02-21.
 */
public abstract class Entry implements Element {
    /**
     * 获取名字
     *
     * @return 名字
     */
    public abstract String getName();

    /**
     * 获取占用空间
     *
     * @return 占用空间
     */
    public abstract int getSize();

    /**
     * 添加
     *
     * @param entry 条目对象
     * @return 返回文件夹对象
     * @throws FileTreatmentException 文件处理异常
     */
    public Entry add(Entry entry) throws FileTreatmentException {
        throw new FileTreatmentException("只有文件夹才能添加条目!");
    }

    /**
     * 使用默认前缀,显示目录条目
     */
    public void printList() {
        printList("");
    }

    /**
     * 指定前缀,显示目录条目
     *
     * @param prefix 前缀
     */
    protected abstract void printList(String prefix);

    @Override
    public String toString() {
        return this.getName() + " (" + this.getSize() + ")";
    }
}
File类
package com.pc.visitor.example;

/**
 * 文件类
 * Created by Switch on 2017-02-21.
 */
public class File extends Entry {
    /**
     * 文件名
     */
    private String name;

    /**
     * 文件占用空间
     */
    private int size;

    /**
     * 构造方法,传入文件名和文件占用空间
     *
     * @param name 文件名
     * @param size 文件占用空间
     */
    public File(String name, int size) {
        this.name = name;
        this.size = size;
    }

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

    @Override
    public int getSize() {
        return this.size;
    }

    @Override
    protected void printList(String prefix) {
        System.out.println(prefix + "/" + this);
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}
Directory类
package com.pc.visitor.example;

import com.pc.abstractfactory.example.factory.Item;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * 文件夹类
 * Created by Switch on 2017-02-21.
 */
public class Directory extends Entry {
    /**
     * 文件夹名
     */
    private String name;
    /**
     * 文件夹中的条目集合
     */
    private List<Entry> entries = new ArrayList<>();

    /**
     * 构造方法,传入文件夹名
     *
     * @param name 文件夹名
     */
    public Directory(String name) {
        this.name = name;
    }

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

    @Override
    public int getSize() {
        int size = 0;
        for (Entry entry : entries) {
            size += entry.getSize();
        }
        return size;
    }

    @Override
    public Entry add(Entry entry) {
        this.entries.add(entry);
        return this;
    }

    @Override
    protected void printList(String prefix) {
        System.out.println(prefix + "/" + this);
        for (Entry entry : entries) {
            // 打印文件夹下的文件列表,以文件夹前缀,文件名作为前缀
            entry.printList(prefix + "/" + this.name);
        }
    }

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

    /**
     * 获取迭代器
     *
     * @return 迭代器
     */
    public Iterator<Entry> iterator() {
        return this.entries.iterator();
    }
}
FileTreatmentException类
package com.pc.visitor.example;

/**
 * 文件处理异常
 * Created by Switch on 2017-02-21.
 */
public class FileTreatmentException extends RuntimeException {
    public FileTreatmentException() {

    }

    public FileTreatmentException(String message) {
        super(message);
    }
}
测试类
package com.pc.visitor.example.test;

import com.pc.visitor.example.Directory;
import com.pc.visitor.example.File;
import com.pc.visitor.example.FileTreatmentException;
import com.pc.visitor.example.ListVisitor;
import org.junit.Test;

/**
 * Visitor Tester.
 *
 * @author Switch
 * @version 1.0
 */
public class VisitorTest {

    /**
     * 测试访问者模式
     */
    @Test
    public void testVisitor() {
        try {
            System.out.println("添加文件和文件夹:");
            System.out.println("Making root entries...");
            Directory rootDir = new Directory("root");
            Directory binDir = new Directory("bin");
            Directory usrDir = new Directory("usr");

            rootDir.add(binDir);
            rootDir.add(usrDir);

            binDir.add(new File("java", 5000));
            binDir.add(new File("javac", 10000));

            rootDir.accept(new ListVisitor());

            System.out.println();
            System.out.println("接着添加文件和文件夹:");
            System.out.println("Making user entries...");
            Directory switchvov = new Directory("switchvov");
            Directory kity = new Directory("kity");
            Directory tom = new Directory("tom");
            Directory bob = new Directory("bob");

            usrDir.add(switchvov);
            usrDir.add(kity);
            usrDir.add(bob);

            switchvov.add(new File("readme.txt", 1000));
            switchvov.add(new File("hello.txt", 400));
            kity.add(new File("find.sh", 5000));

            rootDir.accept(new ListVisitor());
        } catch (FileTreatmentException e) {
            e.printStackTrace();
        }
    }
}
运行结果
添加文件和文件夹:
Making root entries...
/root (15000)
/root/bin (15000)
/root/bin/java (5000)
/root/bin/javac (10000)
/root/usr (0)

接着添加文件和文件夹:
Making user entries...
/root (21400)
/root/bin (15000)
/root/bin/java (5000)
/root/bin/javac (10000)
/root/usr (6400)
/root/usr/switchvov (1400)
/root/usr/switchvov/readme.txt (1000)
/root/usr/switchvov/hello.txt (400)
/root/usr/kity (5000)
/root/usr/kity/find.sh (5000)
/root/usr/bob (0)

访问者模式中的角色

Visitor(访问者)

Visitor角色负责对数据结构中每个具体的元素(ConcreteElement角色)声明一个用于访问xxxxxvisit(XXXXX)方法。visit(XXXXX)是用于处理xxxxx的方法,负责实现该方法的是ConcreteVisitor角色。在案例中,由Visitor类扮演此角色。

ConcreteVisitor(具体的访问者)

ConcreteVisitor角色负责实现Visitor角色所定义的接口(API )。它要实现所有的visit(XXXXX)方法,即实现如何处理每个ConcreteElement角色。在案例中,由ListVisitor类扮演此角色。如同在ListVisitor中, currentdir字段的值不断发生变化一样,随着visit(XXXXX)处理的进行,ConcreteVisitor角色的内部状态也会不断地发生变化。

Element(元素)

Element角色表示Visitor角色的访问对象。它声明了接受访问者的accept方法。accept方法接收到的参数是Visitor角色。在案例中,由Element接口扮演此角色。

ConcreteElement

ConcreteElement角色负责实现Element角色所定义的接口(API)。在案例中,由File类和Directory类扮演此角色。

ObjectStructure(对象结构)

ObjectStructur角色负责处理Element角色的集合。ConcreteVisitor角色为每个Element角色都准备了处理方法。在案例中,由Directory类扮演此角色( 一人分饰两角)。为了让ConcreteVisitor角色可以遍历处理每个Element 角色,在案例中,我们在Directory类中实现了iterator方法。

类图

访问者模式类图

GitHub:DesignPatternStudy

——————参考《图解设计模式》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值