Java设计模式之访问者模式详解
什么是访问者模式?
访问者模式(Visitor Pattern)是一种行为型设计模式,它允许你在不改变各元素类的前提下定义作用于这些元素的新操作。这种模式将操作与对象结构分离,使得我们可以在不修改对象结构的情况下,增加新的操作。
为什么使用访问者模式?
- 实现对象结构和操作的分离
- 增加新的操作很方便
- 集中相关的操作
- 访问者可以积累状态
实现示例
1. 基本实现
// 访问者接口
public interface Visitor {
void visit(ConcreteElementA element);
void visit(ConcreteElementB element);
}
// 元素接口
public interface Element {
void accept(Visitor visitor);
}
// 具体元素A
public class ConcreteElementA implements Element {
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
public String operationA() {
return "ElementA";
}
}
// 具体元素B
public class ConcreteElementB implements Element {
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
public String operationB() {
return "ElementB";
}
}
// 具体访问者1
public class ConcreteVisitor1 implements Visitor {
@Override
public void visit(ConcreteElementA element) {
System.out.println("访问者1访问" + element.operationA());
}
@Override
public void visit(ConcreteElementB element) {
System.out.println("访问者1访问" + element.operationB());
}
}
// 具体访问者2
public class ConcreteVisitor2 implements Visitor {
@Override
public void visit(ConcreteElementA element) {
System.out.println("访问者2访问" + element.operationA());
}
@Override
public void visit(ConcreteElementB element) {
System.out.println("访问者2访问" + element.operationB());
}
}
// 对象结构
public class ObjectStructure {
private List<Element> elements = new ArrayList<>();
public void attach(Element element) {
elements.add(element);
}
public void detach(Element element) {
elements.remove(element);
}
public void accept(Visitor visitor) {
for (Element element : elements) {
element.accept(visitor);
}
}
}2. 文件系统示例
// 文件系统元素接口
public interface FileSystemElement {
void accept(FileVisitor visitor);
}
// 文件访问者接口
public interface FileVisitor {
void visit(File file);
void visit(Directory directory);
}
// 文件类
public class File implements FileSystemElement {
private String name;
private int size;
public File(String name, int size) {
this.name = name;
this.size = size;
}
@Override
public void accept(FileVisitor visitor) {
visitor.visit(this);
}
public String getName() {
return name;
}
public int getSize() {
return size;
}
}
// 目录类
public class Directory implements FileSystemElement {
private String name;
private List<FileSystemElement> elements = new ArrayList<>();
public Directory(String name) {
this.name = name;
}
public void add(FileSystemElement element) {
elements.add(element);
}
@Override
public void accept(FileVisitor visitor) {
visitor.visit(this);
for (FileSystemElement element : elements) {
element.accept(visitor);
}
}
public String getName() {
return name;
}
public List<FileSystemElement> getElements() {
return elements;
}
}
// 大小计算访问者
public class SizeCalculator implements FileVisitor {
private int totalSize = 0;
@Override
public void visit(File file) {
totalSize += file.getSize();
}
@Override
public void visit(Directory directory) {
// 目录本身不占用空间
}
public int getTotalSize() {
return totalSize;
}
}
// 文件列表打印访问者
public class FileLister implements FileVisitor {
private int indent = 0;
@Override
public void visit(File file) {
System.out.println(" ".repeat(indent) + "- " + file.getName());
}
@Override
public void visit(Directory directory) {
System.out.println(" ".repeat(indent) + "+ " + directory.getName());
indent += 2;
}
}3. 使用示例
public class Client {
public static void main(String[] args) {
// 基本示例
ObjectStructure structure = new ObjectStructure();
structure.attach(new ConcreteElementA());
structure.attach(new ConcreteElementB());
Visitor visitor1 = new ConcreteVisitor1();
Visitor visitor2 = new ConcreteVisitor2();
structure.accept(visitor1);
structure.accept(visitor2);
// 文件系统示例
Directory root = new Directory("root");
Directory home = new Directory("home");
Directory docs = new Directory("docs");
root.add(home);
home.add(docs);
docs.add(new File("resume.doc", 1000));
docs.add(new File("photo.jpg", 2000));
SizeCalculator sizeCalc = new SizeCalculator();
root.accept(sizeCalc);
System.out.println("Total size: " + sizeCalc.getTotalSize() + " bytes");
FileLister lister = new FileLister();
root.accept(lister);
}
}访问者模式的优点
- 符合单一职责原则
- 优秀的扩展性
- 集中相关的操作
- 对象结构可以复用
访问者模式的缺点
- 增加新的元素类型困难
- 破坏封装
- 违反依赖倒置原则
适用场景
- 对象结构中的元素类型固定
- 需要对一个对象结构中的对象进行很多不同操作
- 需要避免在元素类中添加新的操作
- 对象结构包含多个类型的对象
与其他模式的关系
- 组合模式:访问者模式经常与组合模式一起使用
- 迭代器模式:可以使用迭代器来遍历对象结构
- 解释器模式:可以使用访问者来解释语法树
总结
访问者模式是一种用于分离对象结构和操作的设计模式。它在需要对固定的对象结构执行多种不同操作时特别有用。虽然这种模式增加了系统的复杂度,但它提供了很好的扩展性和灵活性。
参考资料
- 《Design Patterns: Elements of Reusable Object-Oriented Software》
- 《Head First Design Patterns》
- Java ASM 框架
- Spring Framework 源码
希望这篇文章能帮助您更好地理解Java中的访问者模式。如果您有任何问题,欢迎在评论区讨论!
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 lucky845的博客!
评论