说明
访问者模式(Visitor Pattern)是一种行为型设计模式,它允许在不改变已有对象结构的情况下,定义新的操作。
在软件开发中,有时需要对一个复杂的对象结构中的各个元素进行不同的操作,例如,计算对象结构中元素的总和、对每个元素进行特定的处理等。如果直接在对象结构的类中定义这些操作,将会导致类的耦合度高,代码维护困难。
访问者模式通过将操作封装在一个访问者类中,使得可以在不改变对象结构的情况下,对对象结构中的每个元素应用不同的操作。访问者模式将数据结构和操作分离,提高了系统的可扩展性和可维护性。
组成部分:
- 抽象访问者(Visitor):声明访问对象结构中各个元素的操作方法,通过不同的具体访问者来实现不同的操作。
- 具体访问者(ConcreteVisitor):实现抽象访问者中定义的操作方法,完成对对象结构中不同元素的具体操作。
- 抽象元素(Element):声明接受访问者访问的方法,以及可能被访问者访问的其他方法。
- 具体元素(ConcreteElement):实现抽象元素中声明的方法,确定访问者访问时的具体行为。
- 对象结构(Object Structure):由多个元素组成的复杂对象,提供一个接受访问者访问的方法。
优点:
- 将数据结构与操作分离,提高了可扩展性和可维护性。
- 新增操作时,只需要添加具体访问者类即可,无需修改现有代码。
- 访问者模式符合开闭原则,对扩展开放,对修改关闭。
缺点:
- 增加新的元素类时,需要修改抽象访问者的接口,导致所有具体访问者类都需要进行修改。
使用场景:
- 对象结构相对稳定,但经常需要在对象结构中定义新的操作。
- 需要对一个对象结构中的元素进行多种不相关的操作。
- 对象结构中的元素数目比较少,但其操作频繁变化。
示例
一个常见的实际应用场景是文件系统的访问。假设我们要实现一个文件系统浏览器,可以浏览文件夹和文件,并对其进行不同的操作,例如统计文件夹大小、查找特定文件等。
在这个场景中,我们可以使用访问者模式。具体的实现中,抽象访问者接口可以定义访问文件夹和文件的方法,具体访问者类可以实现这些方法来完成不同的操作。抽象元素接口可以定义接受访问者的方法,具体元素类可以实现这个方法并把访问者传递给具体元素的子元素,以便它们也能接受访问者的访问。
首先,定义抽象访问者接口Visitor
:
public interface Visitor {
void visit(File file);
void visit(Folder folder);
}
然后,定义抽象元素接口Element
:
public interface Element {
void accept(Visitor visitor);
}
接下来,实现具体的元素类File
和Folder
,并在实现类中实现接受访问者的方法:
public class File implements Element {
private String name;
private int size;
public File(String name, int size) {
this.name = name;
this.size = size;
}
public String getName() {
return name;
}
public int getSize() {
return size;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
public class Folder implements Element {
private String name;
private List<Element> children;
public Folder(String name) {
this.name = name;
this.children = new ArrayList<>();
}
public void addChild(Element element) {
children.add(element);
}
public String getName() {
return name;
}
public List<Element> getChildren() {
return children;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
for (Element child : children) {
child.accept(visitor);
}
}
}
然后,实现具体的访问者类SizeVisitor
,用于统计文件夹大小:
public class SizeVisitor implements Visitor {
private int totalSize;
public int getTotalSize() {
return totalSize;
}
@Override
public void visit(File file) {
totalSize += file.getSize();
}
@Override
public void visit(Folder folder) {
// 访问文件夹时不做任何操作
}
}
最后,我们可以创建文件和文件夹对象,并使用访问者来对文件系统进行操作:
public class VisitorExample {
public static void main(String[] args) {
// 创建文件系统
Folder root = new Folder("root");
File file1 = new File("file1.txt", 10);
File file2 = new File("file2.txt", 20);
Folder folder1 = new Folder("folder1");
File file3 = new File("file3.txt", 15);
File file4 = new File("file4.txt", 25);
// 构建文件系统结构
root.addChild(file1);
root.addChild(file2);
root.addChild(folder1);
folder1.addChild(file3);
folder1.addChild(file4);
// 创建访问者
SizeVisitor visitor = new SizeVisitor();
// 让访问者访问文件系统
root.accept(visitor);
// 获取文件夹大小
System.out.println("Total Size: " + visitor.getTotalSize());
}
}
运行结果
Total Size: 70