“ 访问者设计模式学习心得分享”
适用于结构相对稳定的条目或者xxx对比时,其它相对稳定的层级组织架构使用该模式是OK滴
如条目中文件和文件夹的访问,男人和女人对同一件事情不同反应的对比
条目只分为文件和文件夹,人只分为男人和女人,这称之为结构稳定。
talk is cheap ,show me the code. 先看一张类图
jdk 使用到的访问者模式示例
java.nio.file.FileVisitor
java.nio.file.Files#walkFileTree
Path directory = Paths.get("target/perf-logs/");
//作用:删除文件下的所有目录及文件,并将该文件夹也删掉
if (Files.exists(directory)) {
Files.walkFileTree(directory, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
Files.delete(file);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
Files.delete(dir);
return FileVisitResult.CONTINUE;
}
});
自定义MyElement 接口
package design_pattern.visitor.visitor2;
/**
* 元素接口
*/
public interface MyElement {
void accept(Visitor visitor);
}
MyEntry 条目抽象类
package design_pattern.visitor.visitor2;
import java.util.Iterator;
/**
* 条目抽象类
*/
public abstract class MyEntry implements MyElement {
public abstract String getName();
public abstract int getSize();
public abstract void printList(String prefix);
public void printList() {
printList("");
}
public MyEntry add(MyEntry entry) throws RuntimeException {
throw new RuntimeException();
}
public Iterator iterator() throws RuntimeException {
throw new RuntimeException();
}
public String toString() {
return getName() + "<" + getSize() + ">";
}
}
MyFile 自定义文件类
package design_pattern.visitor.visitor2;
/**
* 自定义文件类
*/
public class MyFile extends MyEntry {
private String name;
private int size;
public MyFile(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 printList(String prefix) {
System.out.println(prefix + "/" + this);
}
@Override
public void accept(Visitor visitor) {
//重点代码
visitor.visit(this);
}
}
MyDirectory 自定义目录类
package design_pattern.visitor.visitor2;
import java.util.ArrayList;
import java.util.Iterator;
/**
* 自定义目录类
*/
public class MyDirectory extends MyEntry {
private String name;
private ArrayList entrys = new ArrayList();
public MyDirectory(String name) {
this.name = name;
}
@Override
public String getName() {
return name;
}
@Override
public int getSize() {
int size = 0;
Iterator it = entrys.iterator();
while (it.hasNext()) {
size += ((MyEntry) it.next()).getSize();
}
return size;
}
@Override
public MyEntry add(MyEntry entry) {
entrys.add(entry);
return this;
}
@Override
public Iterator iterator() {
return entrys.iterator();
}
@Override
public void printList(String prefix) {
System.out.println(prefix + "/" + this);
//用for 遍历迭代器写法 快捷键itco,今晚刚发现滴
for (Iterator iterator = entrys.iterator(); iterator.hasNext(); ) {
MyEntry entry = (MyEntry) iterator.next();
entry.printList(prefix + "/" + name);
}
}
@Override
public void accept(Visitor visitor) {
//重点代码
visitor.visit(this);
}
}
Visitor 抽象访问者
package design_pattern.visitor.visitor2;
/**
* 抽象访问者
*/
public abstract class Visitor {
/**
* 访问文件
* @param myFile
*/
public abstract void visit(MyFile myFile);
/**
* 访问目录
*
* @param directory
*/
public abstract void visit(MyDirectory directory);
}
FileFoundVisitor 查找指定文件访问者
package design_pattern.visitor.visitor2;
import java.util.ArrayList;
import java.util.Iterator;
/**
* 查找指定文件访问者
*/
public class FileFoundVisitor extends Visitor {
private String currentDir = "";
private String suffix;
private ArrayList files = new ArrayList();
public FileFoundVisitor(String suffix) {
this.suffix = suffix;
}
@Override
public void visit(MyFile myFile) {
if (myFile.getName().endsWith(suffix)) {
files.add(currentDir + "/" + myFile);
}
}
public void visit(MyDirectory directory) {
//先保存当前目录
String saveDir = currentDir;
//当前目录被改变
currentDir += ("/" + directory.getName());
Iterator it = directory.iterator();
while (it.hasNext()) {
MyEntry entry = (MyEntry) it.next();
entry.accept(this);
}
//将当前目录改回来
currentDir = saveDir;
}
public Iterator getFoundFiles() {
return files.iterator();
}
}
ListVisitor 查找所有文件访问者
package design_pattern.visitor.visitor2;
import java.util.Iterator;
/**
* 查找所有文件访问者
*/
public class ListVisitor extends Visitor {
String currentDir = "";
public void visit(MyFile myFile) {
System.out.println(currentDir + "/" + myFile);
}
public void visit(MyDirectory directory) {
System.out.println(currentDir + "/" + directory);
String saveDir = currentDir;
currentDir += ("/" + directory.getName());
Iterator it = directory.iterator();
while (it.hasNext()) {
MyEntry entry = (MyEntry) it.next();
entry.accept(this);
}
currentDir = saveDir;
}
}
ObjectStructure 对象结构
package design_pattern.visitor.visitor2;
import java.util.ArrayList;
import java.util.Iterator;
/**
* 对象结构
*/
public class ObjectStructure extends ArrayList<MyElement> implements MyElement {
@Override
public void accept(Visitor visitor) {
//调用ArrayList的iterator方法
for (Iterator<MyElement> iterator = this.iterator(); iterator.hasNext(); ) {
MyElement element = iterator.next();
element.accept(visitor);
}
}
}
Main 客户端
package design_pattern.visitor.visitor2;
import java.util.Iterator;
/**
* 登场角色
* >元素
* >>条目
* |--文件
* |--文件夹
* >访问者
* |-- 查找指定文件访问者
* |-- 查找所有文件访问者
* >对象结构【其实内部就是个封装了ArrayList的容器,加了个accept方法】
*
*
* 使用场景:当出现有组织结构、树形结构、多层级访问、问答场景 需要访问各个条目时,可以考虑用使用访问者设计模式
* 相关模式:
* 1.迭代器设计模式
* 2.组合设计模式
*
* 另外:
* MyDirectory#printList(java.lang.String)方法 为我们提供了一种新的递归访问模型
* 另外一种新的思路就是像本visitor模式一样使用【双重分发】
*
* public void accept(Visitor visitor) {
* visitor.visit(this);
* }
*
* 他们是相反的关系,element接收visitor,visitor访问element,
* 这种消息分发的方式被称为双重分发【double dispatch】
*
* 为什么要弄得这么复复杂?
* visitor模式的主要目的是将【处理】从数据结构中分离出来,
* 【保存数据结构】和【以数据结构为基础】进行处理是两码事
* 缺点:
* visitor模式适用于元素基本不变的情况
* 添加新的元素非常麻烦
*/
public class Main {
public static void main(String[] args) {
//准备数据
MyDirectory root = getData();
//处理数据 可以替换为doData 、doData1、doData2
doData3(root);
}
private static void doData3(MyDirectory root) {
ObjectStructure objectStructure = new ObjectStructure();
objectStructure.add(root);
objectStructure.add(new MyFile("我是小孩也是王", 1));
objectStructure.accept(new ListVisitor());
}
/**
* 处理数据1:全部访问
*
* @param root
*/
private static void doData(MyDirectory root) {
root.accept(new ListVisitor());
}
/**
* 处理数据2:访问数据并查找指定格式的文文件
*
* @param root
*/
private static void doData2(MyDirectory root) {
FileFoundVisitor visitor = new FileFoundVisitor(".psd");
root.accept(visitor);
//打印获取到的指定文件
Iterator it = visitor.getFoundFiles();
while (it.hasNext()) {
System.out.println(it.next());
}
}
private static MyDirectory getData() {
MyDirectory root = new MyDirectory("根目录");
MyDirectory life = new MyDirectory("我的生活");
MyFile eat = new MyFile("吃火锅.txt", 100);
MyFile sleep = new MyFile("睡觉.html", 100);
MyFile study = new MyFile("学习.txt", 100);
life.add(eat);
life.add(sleep);
life.add(study);
MyDirectory work = new MyDirectory("我的工作");
MyFile write = new MyFile("写博客.doc", 200);
MyFile paper = new MyFile("写论文.html", 200);
MyFile homework = new MyFile("写家庭作业.docx", 200);
work.add(write);
work.add(paper);
work.add(homework);
MyDirectory relax = new MyDirectory("我的休闲");
MyFile music = new MyFile("听听音乐.js", 200);
MyFile walk = new MyFile("出去转转.psd", 200);
relax.add(music);
relax.add(walk);
MyDirectory read = new MyDirectory("我的阅读");
MyFile book = new MyFile("学习书籍.psd", 200);
MyFile novel = new MyFile("娱乐小说.txt", 200);
read.add(book);
read.add(novel);
root.add(life);
root.add(work);
root.add(relax);
root.add(read);
return root;
}
}
总结
登场角色
元素 >>条目 |–文件 |–文件夹
访问者 |-- 查找指定文件访问者 |-- 查找所有文件访问者
对象结构【其实内部就是个封装了ArrayList的容器,加了个accept方法】
使用场景:当出现有组织结构、树形结构、多层级访问、问答场景
需要访问各个条目时,可以考虑用使用访问者设计模式
相关模式:
1.迭代器设计模式
2.组合设计模式
另外:MyDirectory#printList(java.lang.String)方法 为我们提供了一种新的递归访问模型 另外一种新的思路就是像本visitor模式一样使用【双重分发】
public void accept(Visitor visitor) {
visitor.visit(this);
}
他们是相反的关系,element接收visitor,visitor访问element,
这种消息分发的方式被称为双重分发【double dispatch】
为什么要弄得这么复复杂?
visitor模式的主要目的是将【处理】从数据结构中分离出来,
【保存数据结构】和【以数据结构为基础】进行处理是两码事
缺点:
visitor模式适用于元素基本不变的情况
添加新的元素非常麻烦
总结:本片文章我们主要描述了什么是访问者visitor设计模式,以及使用场景。以及本设计模式相关联的模式:如迭代器设计模式和组合设计模式
,这些都是相互关联的,设计模式是一种套路、是一种模板、是一种经验,是一种角色扮演,每个模式都有其登场角色,我们自己在设计和编程时,要思考一下学习过的设计模,是否适用于当前场景。
你现在多努力一份,以后老婆就多漂亮一分,加油吧,少年!!!