访问者模式(Visitor Pattern),意在解耦操作和对象本身。从概念上并不能理解清楚,我们在代码示例中结合讲解。
今天以文件操作为例,来讲解访问者模式。
我们现在要实现多种类型的文件读取功能,比如从word、pdf、excel中读取文件内容加载到程序中。
正常逻辑是实现三个类,分别是ReadWord、ReadPdf、ReadExcel。
代码如下:
1、定义抽象父类
package com.itzhimei.study.design.visitor;
/**
* @Auther: www.itzhimei.com
* @Description: 文件读取抽象类
*/
public abstract class ReadFile {
String filePath;
public ReadFile(String filePath) {
this.filePath = filePath;
}
/**
* 读取文件
*/
public abstract void readFileContent();
}
2、定义三种文件读取功能类
package com.itzhimei.study.design.visitor;
/**
* @Auther: www.itzhimei.com
* @Description: 读取word
*/
public class ReadWord extends ReadFile {
public ReadWord(String filePath) {
super(filePath);
}
@Override
public void readFileContent() {
System.out.println("读取Word到程序");
}
}
package com.itzhimei.study.design.visitor;
/**
* @Auther: www.itzhimei.com
* @Description: 读取pdf
*/
public class ReadPdf extends ReadFile {
public ReadPdf(String filePath) {
super(filePath);
}
@Override
public void readFileContent() {
System.out.println("读取Pdf到程序");
}
}
package com.itzhimei.study.design.visitor;
/**
* @Auther: www.itzhimei.com
* @Description: 读取excel
*/
public class ReadExcel extends ReadFile {
public ReadExcel(String filePath) {
super(filePath);
}
@Override
public void readFileContent() {
System.out.println("读取Excel到程序");
}
}
用以上代码就能实现我们的需求了,而且按照不同文件,也拆分了不同功能类,但是如果我们不仅仅是从文件中读取内容,还需要其他功能,比如文件内容压缩,那么就意味着,三个功能类,都要改一遍,添加压缩功能方法,也就是说,随着功能增加,我们的代码要频繁修改,这没有达到开闭原则,所以我们要用访问者模式进行改进。
我们在一开始就介绍了访问者模式的特点,那就是将操作和对象解耦。所以我们要将行为抽出来,放在一个单独的类中,我们先对上面的代码进行调整,达到将操作和对象解耦。
调整方式是将读取文件内容的功能方法,放到一个外部类Extractor中,由Extractor类来负责实际读取文件的逻辑。看代码:
1、定义抽象文件读取类
package com.itzhimei.study.design.visitor;
/**
* @Auther: www.itzhimei.com
* @Description: 文件读取抽象类
*/
public abstract class ReadFile {
String filePath;
public ReadFile(String filePath) {
this.filePath = filePath;
}
/**
* 读取文件
*/
//public abstract void readFileContent();
public abstract void accept(Extractor extractor);
}
2、定义各种文件读取的具体实现,这里实现了分别定义了ReadWord、ReadPdf和ReadExcel的读取逻辑,这三方方法是从原来的对应类中抽取出来的方法。
这个执行器中采用了方法重载,运行时,根据不同参数,执行不同的方法。
package com.itzhimei.study.design.visitor;
/**
* @Auther: www.itzhimei.com
* @Description: 文件读取执行器
*/
public class Extractor {
public void readFileContent(ReadWord word) {
System.out.println("读取Word到程序");
}
public void readFileContent(ReadPdf word) {
System.out.println("读取Pdf到程序");
}
public void readFileContent(ReadExcel word) {
System.out.println("读取Excel到程序");
}
}
3、定义三种文件读取功能类,原来的类中的readFileContent方法已经不需要了,替换的方法是:accept(Extractor extractor),这里采用组合的模式,在ReadWord的accept方法,使用了Extractor,由Extractor在运行时决定执行哪个方法。
package com.itzhimei.study.design.visitor;
/**
* @Auther: www.itzhimei.com
* @Description: 读取word
*/
public class ReadWord extends ReadFile {
public ReadWord(String filePath) {
super(filePath);
}
/*@Override
public void readFileContent() {
System.out.println("读取Word到程序");
}*/
@Override
public void accept(Extractor extractor) {
extractor.readFileContent(this);
}
}
package com.itzhimei.study.design.visitor;
/**
* @Auther: www.itzhimei.com
* @Description: 读取pdf
*/
public class ReadPdf extends ReadFile {
public ReadPdf(String filePath) {
super(filePath);
}
/*@Override
public void readFileContent() {
System.out.println("读取Pdf到程序");
}*/
@Override
public void accept(Extractor extractor) {
extractor.readFileContent(this);
}
}
package com.itzhimei.study.design.visitor;
import java.util.concurrent.Executors;
/**
* @Auther: www.itzhimei.com
* @Description: 读取excel
*/
public class ReadExcel extends ReadFile {
public ReadExcel(String filePath) {
super(filePath);
}
/*@Override
public void readFileContent() {
System.out.println("读取Excel到程序");
}*/
@Override
public void accept(Extractor extractor) {
extractor.readFileContent(this);
}
}
4、测试
package com.itzhimei.study.design.visitor;
import java.util.ArrayList;
import java.util.List;
/**
* @Auther: www.itzhimei.com
* @Description:
*/
public class Client {
public static void main(String[] args) {
Extractor extractor = new Extractor();
List<ReadFile> rfs = new ArrayList<>();
rfs.add(new ReadWord("文件路径"));
rfs.add(new ReadPdf("文件路径"));
rfs.add(new ReadExcel("文件路径"));
rfs.forEach(x -> x.accept(extractor));
}
}
输出:
读取Word到程序
读取Pdf到程序
读取Excel到程序
类结构图:
到这里,第二个版本已经是一个访问者模式了,我们在下一节来演示一下,基于访问者模式的多功能扩展,也就是上面说的再扩展一个文件内容压缩功能。