Java访问者模式案例
访问者模式(Visitor Pattern)是一种行为设计模式,它允许在不修改现有类结构的情况下,为这些类添加新的操作。

核心角色
- 抽象访问者(Visitor):声明每个具体元素类的访问方法
- 具体访问者(ConcreteVisitor):实现每个访问方法
- 抽象元素(Element):声明接受访问者的方法
- 具体元素(ConcreteElement):实现接受方法
- 对象结构(ObjectStructure):管理元素集合
完整案例:文件系统分析器
import java.util.ArrayList;
import java.util.List;
// ============ 抽象元素 ============
interface FileElement {
void accept(Visitor visitor);
}
// ============ 具体元素 ============
// 文本文件
class TextFile implements FileElement {
private String name;
private int wordCount;
public TextFile(String name, int wordCount) {
this.name = name;
this.wordCount = wordCount;
}
public String getName() { return name; }
public int getWordCount() { return wordCount; }
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
// 图片文件
class ImageFile implements FileElement {
private String name;
private int width;
private int height;
public ImageFile(String name, int width, int height) {
this.name = name;
this.width = width;
this.height = height;
}
public String getName() { return name; }
public int getWidth() { return width; }
public int getHeight() { return height; }
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
// 文件夹
class Folder implements FileElement {
private String name;
private List<FileElement> elements = new ArrayList<>();
public Folder(String name) {
this.name = name;
}
public String getName() { return name; }
public void addElement(FileElement element) {
elements.add(element);
}
public List<FileElement> getElements() {
return elements;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
// ============ 抽象访问者 ============
interface Visitor {
void visit(TextFile textFile);
void visit(ImageFile imageFile);
void visit(Folder folder);
}
// ============ 具体访问者1:文件信息收集器 ============
class FileInfoCollector implements Visitor {
private StringBuilder report = new StringBuilder();
@Override
public void visit(TextFile textFile) {
report.append(String.format("文本文件: %s (字数: %d)%n",
textFile.getName(), textFile.getWordCount()));
}
@Override
public void visit(ImageFile imageFile) {
report.append(String.format("图片文件: %s (尺寸: %dx%d)%n",
imageFile.getName(), imageFile.getWidth(), imageFile.getHeight()));
}
@Override
public void visit(Folder folder) {
report.append(String.format("文件夹: %s%n", folder.getName()));
for (FileElement element : folder.getElements()) {
element.accept(this);
}
}
public String getReport() {
return report.toString();
}
}
// ============ 具体访问者2:文件大小计算器 ============
class FileSizeCalculator implements Visitor {
private long totalSize = 0;
@Override
public void visit(TextFile textFile) {
// 假设每个字符占用2字节
totalSize += textFile.getWordCount() * 2;
System.out.println(textFile.getName() + " 大小: " + totalSize + " 字节");
}
@Override
public void visit(ImageFile imageFile) {
// 假设每个像素占用3字节
totalSize += imageFile.getWidth() * imageFile.getHeight() * 3;
System.out.println(imageFile.getName() + " 大小: " + totalSize + " 字节");
}
@Override
public void visit(Folder folder) {
System.out.println("进入文件夹: " + folder.getName());
for (FileElement element : folder.getElements()) {
element.accept(this);
}
}
public long getTotalSize() {
return totalSize;
}
}
// ============ 对象结构 ============
class FileSystem {
private List<FileElement> elements = new ArrayList<>();
public void addElement(FileElement element) {
elements.add(element);
}
public void accept(Visitor visitor) {
for (FileElement element : elements) {
element.accept(visitor);
}
}
}
// ============ 客户端 ============
public class VisitorPatternDemo {
public static void main(String[] args) {
// 创建文件系统结构
Folder root = new Folder("项目文档");
Folder srcFolder = new Folder("源代码");
TextFile mainJava = new TextFile("Main.java", 500);
TextFile utilsJava = new TextFile("Utils.java", 300);
srcFolder.addElement(mainJava);
srcFolder.addElement(utilsJava);
Folder docsFolder = new Folder("文档");
ImageFile diagram = new ImageFile("架构图.png", 1920, 1080);
TextFile readme = new TextFile("README.md", 100);
docsFolder.addElement(diagram);
docsFolder.addElement(readme);
root.addElement(srcFolder);
root.addElement(docsFolder);
// 使用不同的访问者
System.out.println("===== 文件信息报告 =====");
FileInfoCollector collector = new FileInfoCollector();
root.accept(collector);
System.out.println(collector.getReport());
System.out.println("\n===== 文件大小计算 =====");
FileSizeCalculator calculator = new FileSizeCalculator();
root.accept(calculator);
System.out.println("总大小: " + calculator.getTotalSize() + " 字节");
}
}
简化版案例:动物园访客
// 抽象动物
interface Animal {
void accept(ZooVisitor visitor);
}
// 具体动物
class Lion implements Animal {
private String name;
public Lion(String name) { this.name = name; }
public String getName() { return name; }
@Override
public void accept(ZooVisitor visitor) {
visitor.visit(this);
}
}
class Elephant implements Animal {
private String name;
public Elephant(String name) { this.name = name; }
public String getName() { return name; }
@Override
public void accept(ZooVisitor visitor) {
visitor.visit(this);
}
}
// 抽象访问者
interface ZooVisitor {
void visit(Lion lion);
void visit(Elephant elephant);
}
// 具体访问者:喂食员
class Feeder implements ZooVisitor {
@Override
public void visit(Lion lion) {
System.out.println("喂食员给狮子 " + lion.getName() + " 喂肉");
}
@Override
public void visit(Elephant elephant) {
System.out.println("喂食员给大象 " + elephant.getName() + " 喂水果");
}
}
// 具体访问者:清洁员
class Cleaner implements ZooVisitor {
@Override
public void visit(Lion lion) {
System.out.println("清洁员清理狮子 " + lion.getName() + " 的笼子");
}
@Override
public void visit(Elephant elephant) {
System.out.println("清洁员清理大象 " + elephant.getName() + " 的场地");
}
}
// 使用示例
public class ZooVisitorDemo {
public static void main(String[] args) {
List<Animal> animals = new ArrayList<>();
animals.add(new Lion("辛巴"));
animals.add(new Elephant("阿里"));
ZooVisitor feeder = new Feeder();
ZooVisitor cleaner = new Cleaner();
System.out.println("=== 喂食时间 ===");
for (Animal animal : animals) {
animal.accept(feeder);
}
System.out.println("\n=== 清洁时间 ===");
for (Animal animal : animals) {
animal.accept(cleaner);
}
}
}
使用Lambda表达式优化
import java.util.function.Consumer;
// 简化版访问者模式
interface Shape {
void accept(ShapeVisitor visitor);
}
class Circle implements Shape {
private double radius;
public Circle(double radius) { this.radius = radius; }
public double getRadius() { return radius; }
@Override
public void accept(ShapeVisitor visitor) {
visitor.visit(this);
}
}
class Rectangle implements Shape {
private double width, height;
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
public double getWidth() { return width; }
public double getHeight() { return height; }
@Override
public void accept(ShapeVisitor visitor) {
visitor.visit(this);
}
}
// 使用函数式接口
@FunctionalInterface
interface ShapeVisitor {
void visit(Shape shape);
}
// 使用示例
public class LambdaVisitorDemo {
public static void main(String[] args) {
List<Shape> shapes = new ArrayList<>();
shapes.add(new Circle(5));
shapes.add(new Rectangle(3, 4));
// 使用Lambda创建访问者
ShapeVisitor areaCalc = shape -> {
if (shape instanceof Circle) {
Circle circle = (Circle) shape;
System.out.println("圆面积: " + Math.PI * circle.getRadius() * circle.getRadius());
} else if (shape instanceof Rectangle) {
Rectangle rect = (Rectangle) shape;
System.out.println("矩形面积: " + rect.getWidth() * rect.getHeight());
}
};
shapes.forEach(shape -> shape.accept(areaCalc));
}
}
访问者模式的优势
- 开闭原则:可以轻松添加新操作而不修改现有类
- 单一职责:将相关操作集中在一个访问者中
- 扩展性好:新操作只需添加新的访问者类
适用场景
- 对象结构稳定但需要添加新操作
- 需要对不同类型对象执行不相关的操作
- 一个对象结构包含很多类,且这些类的接口不同
注意事项
- 增加新元素类型困难(需要修改所有访问者)
- 可能违反依赖倒置原则
- 适用于结构稳定但操作多变的场景
访问者模式将数据结构与操作分离,使得算法可以独立于数据结构变化。