访问数据结构并处理数据
数据结构中保存着许多的元素,在对这些元素处理的时候,将处理代码放入数据结构中,当每次增加或者修改处理方法时,都必须将数据结构的类进行修改,因此,我们需要用到访问者模式,数据结构和数据处理被分离出来,创建一个访问者专门来访问数据结构中的元素,把元素的处理交给访问者类,这样增加或修改时不需要改动数据结构。
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角色的集合,这样不仅仅可以做到同一个数据结构可以有不同的处理方案,而且可以不同的数据结构共用同一个处理方法。
此模式将数据结构与对数据的处理分开,在上层以父类或者接口来进行指定规则。
开闭原则:
- 对扩展开放.
- 对修改关闭.