访问者模式,可以说是23种设计模式中最复杂、最难以理解的一种了,并且使用频率不高,对于不常使用的设计模式,我会用较小的篇幅去介绍。
概述
访问者模式:(Visitor Design Pattern)允许一个或者多个操作应用到一组对象上,解耦操作和对象本身。
访问者模式是一种将数据操作和数据结构分离的设计模式。
何时使用:
- 对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。
- 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作"污染"这些对象的类,也不希望在增加新操作时修改这些类。
角色组成:
-
Visitor: 接口或者抽象类,定义了对每个 Element 访问的行为,它的参数就是被访问的元素,它的方法个数理论上与元素的个数是一样的,因此,访问者模式要求元素的类型要稳定,如果经常添加、移除元素类,必然会导致频繁地修改 Visitor 接口,如果出现这种情况,则说明不适合使用访问者模式。
-
ConcreteVisitor: 具体的访问者,它需要给出对每一个元素类访问时所产生的具体行为。
-
Element: 元素接口或者抽象类,它定义了一个接受访问者(accept)的方法,其意义是指每一个元素都要可以被访问者访问。
-
ElementA、ElementB: 具体的元素类,它提供接受访问的具体实现,而这个具体的实现,通常情况下是使用访问者提供的访问该元素类的方法。
-
ObjectStructure: 定义当中所提到的对象结构,对象结构是一个抽象表述,它内部管理了元素集合,并且可以迭代这些元素提供访问者访问。
实例代码
假设我们需要实现这样一个功能:从 PDF、PPT、Word 资源文件中的文本内容抽取出来放到 txt 文件中,同时还要支持压缩,并且还要方便以后扩展新功能,不违反开闭原则、单一职责原则。此时就可以用到访问者模式。
public abstract class ResourceFile {
protected String filePath;
public ResourceFile(String filePath) {
this.filePath = filePath;
}
abstract public void accept(Visitor visitor);
}
public interface Visitor {
void visit(PdfFile pdfFile);
void visit(PptFile pptFile);
void visit(WordFile wordFile);
}
public class Compressor implements Visitor {
@Override
public void visit(PdfFile pdfFile) {
System.out.println("Compress PDF");
}
@Override
public void visit(PptFile pptFile) {
System.out.println("Compress PPT");
}
@Override
public void visit(WordFile wordFile) {
System.out.println("Compress WORD");
}
}
public class Extractor implements Visitor {
@Override
public void visit(PdfFile pdfFile) {
System.out.println("Extract PDF");
}
@Override
public void visit(PptFile pptFile) {
System.out.println("Extract PPT");
}
@Override
public void visit(WordFile wordFile) {
System.out.println("Extract WORD");
}
}
public class PdfFile extends ResourceFile {
public PdfFile(String filePath) {
super(filePath);
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
public class PptFile extends ResourceFile {
public PptFile(String filePath) {
super(filePath);
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
public class WordFile extends ResourceFile {
public WordFile(String filePath) {
super(filePath);
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
使用时:
public class Test {
public static void main(String[] args) {
Extractor extractor = new Extractor();
List<ResourceFile> resourceFiles = listAllResourceFiles();
for (ResourceFile resourceFile : resourceFiles) {
resourceFile.accept(extractor);
}
Compressor compressor = new Compressor();
for (ResourceFile resourceFile : resourceFiles) {
resourceFile.accept(compressor);
}
}
private static List<ResourceFile> listAllResourceFiles() {
List<ResourceFile> resourceFiles = new ArrayList<>();
resourceFiles.add(new PdfFile("a.pdf"));
resourceFiles.add(new WordFile("b.word"));
resourceFiles.add(new PptFile("c.ppt"));
return resourceFiles;
}
}
结果:
Extract PDF
Extract WORD
Extract PPT
Compress PDF
Compress WORD
Compress PPT
如果要继续添加新的功能,类似前面的提取、压缩功能,我们只需要实现一个类似 Extractor、Compressor 的类,继承 Visitor 接口,在其中定义三个重载函数。资源文件类不需要做任何修改。
总结
访问者模式的优点:
- 各角色职责分离,符合单一职责原则
- 具有优秀的扩展性
- 使得数据结构和作用于结构上的操作解耦,使得操作集合可以独立变化
- 灵活性