Java访问者模式详解

访问者模式详解

一、模式定义

访问者模式(Visitor Pattern)表示一个作用于某对象结构中的各元素的操作,它使你可以在不改变各元素类的前提下定义作用于这些元素的新操作。

二、核心结构

1. 抽象元素接口

复制代码
public interface Element {
    void accept(Visitor visitor);
}

2. 具体元素类

复制代码
public class ConcreteElementA implements Element {
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
    
    public String operationA() {
        return "具体元素A的操作";
    }
}

public class ConcreteElementB implements Element {
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
    
    public String operationB() {
        return "具体元素B的操作";
    }
}

3. 抽象访问者接口

复制代码
public interface Visitor {
    void visit(ConcreteElementA element);
    void visit(ConcreteElementB element);
}

4. 具体访问者实现

复制代码
public class ConcreteVisitor1 implements Visitor {
    public void visit(ConcreteElementA element) {
        System.out.println("访问者1访问" + element.operationA());
    }
    
    public void visit(ConcreteElementB element) {
        System.out.println("访问者1访问" + element.operationB());
    }
}

public class ConcreteVisitor2 implements Visitor {
    public void visit(ConcreteElementA element) {
        System.out.println("访问者2访问" + element.operationA());
    }
    
    public void visit(ConcreteElementB element) {
        System.out.println("访问者2访问" + element.operationB());
    }
}

5. 对象结构类

复制代码
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);
        }
    }
}

三、完整示例:编译器系统

1. AST节点定义

复制代码
public interface ASTNode extends Element {
    String getNodeName();
}

public class VariableNode implements ASTNode {
    private String name;
    
    public VariableNode(String name) {
        this.name = name;
    }
    
    public String getName() {
        return name;
    }
    
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
    
    public String getNodeName() {
        return "Variable:" + name;
    }
}

public class AssignmentNode implements ASTNode {
    private ASTNode left;
    private ASTNode right;
    
    public AssignmentNode(ASTNode left, ASTNode right) {
        this.left = left;
        this.right = right;
    }
    
    public ASTNode getLeft() {
        return left;
    }
    
    public ASTNode getRight() {
        return right;
    }
    
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
    
    public String getNodeName() {
        return "Assignment";
    }
}

2. 访问者接口

复制代码
public interface ASTVisitor extends Visitor {
    void visit(VariableNode node);
    void visit(AssignmentNode node);
}

3. 具体访问者

复制代码
// 类型检查访问者
public class TypeCheckVisitor implements ASTVisitor {
    public void visit(VariableNode node) {
        System.out.println("类型检查变量: " + node.getName());
    }
    
    public void visit(AssignmentNode node) {
        System.out.println("检查赋值语句类型兼容性");
        node.getLeft().accept(this);
        node.getRight().accept(this);
    }
}

// 代码生成访问者
public class CodeGenVisitor implements ASTVisitor {
    public void visit(VariableNode node) {
        System.out.println("生成变量代码: " + node.getName());
    }
    
    public void visit(AssignmentNode node) {
        System.out.println("生成赋值代码");
        node.getLeft().accept(this);
        node.getRight().accept(this);
    }
}

4. 客户端使用

复制代码
public class CompilerClient {
    public static void main(String[] args) {
        ASTNode ast = new AssignmentNode(
            new VariableNode("x"),
            new VariableNode("y")
        );
        
        ObjectStructure structure = new ObjectStructure();
        structure.attach(ast);
        
        // 类型检查
        structure.accept(new TypeCheckVisitor());
        
        // 代码生成
        structure.accept(new CodeGenVisitor());
    }
}

四、高级应用:文件系统访问

1. 文件系统元素

复制代码
public interface FileSystemElement extends Element {
    String getName();
    long getSize();
}

public class File implements FileSystemElement {
    private String name;
    private long size;
    
    public File(String name, long size) {
        this.name = name;
        this.size = size;
    }
    
    public String getName() {
        return name;
    }
    
    public long getSize() {
        return size;
    }
    
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

public class Directory implements FileSystemElement {
    private String name;
    private List<FileSystemElement> children = new ArrayList<>();
    
    public Directory(String name) {
        this.name = name;
    }
    
    public void add(FileSystemElement element) {
        children.add(element);
    }
    
    public String getName() {
        return name;
    }
    
    public long getSize() {
        long total = 0;
        for (FileSystemElement child : children) {
            total += child.getSize();
        }
        return total;
    }
    
    public void accept(Visitor visitor) {
        visitor.visit(this);
        for (FileSystemElement child : children) {
            child.accept(visitor);
        }
    }
}

2. 文件系统访问者

复制代码
public interface FileSystemVisitor extends Visitor {
    void visit(File file);
    void visit(Directory directory);
}

// 大小计算访问者
public class SizeCalculatorVisitor implements FileSystemVisitor {
    private long totalSize = 0;
    
    public void visit(File file) {
        totalSize += file.getSize();
    }
    
    public void visit(Directory directory) {
        // 目录本身不占空间
    }
    
    public long getTotalSize() {
        return totalSize;
    }
}

// 文件查找访问者
public class FileSearchVisitor implements FileSystemVisitor {
    private String searchName;
    private List<File> results = new ArrayList<>();
    
    public FileSearchVisitor(String searchName) {
        this.searchName = searchName;
    }
    
    public void visit(File file) {
        if (file.getName().contains(searchName)) {
            results.add(file);
        }
    }
    
    public void visit(Directory directory) {
        // 不处理目录
    }
    
    public List<File> getResults() {
        return results;
    }
}

五、模式优势

  1. 增加新操作容易
  2. 将相关操作集中在一个访问者中
  3. 访问者可以跨多个类层次结构操作
  4. 符合单一职责原则开闭原则

六、适用场景

  1. 对象结构包含多个类,且需要对这些类进行不同操作
  2. 需要对对象结构中的类进行不相关的操作
  3. 对象结构很少变化但经常需要新增操作
  4. 需要跨多个类层次结构的操作

七、注意事项

  1. 破坏封装性,要求元素公开内部状态
  2. 增加新的元素类困难
  3. 对象结构变化会导致所有访问者修改
  4. 可能违反依赖倒置原则

八、最佳实践

  1. 为对象结构提供稳定的接口
  2. 使用访问者组合模式处理复杂结构
  3. 考虑使用反射减少访问者接口方法
  4. 为常用操作提供默认访问者实现
  5. 合理设计访问者与被访问元素的交互方式

九、完整示例代码结构

java 复制代码
src/
├── main/
│ ├── java/
│ │ ├── visitor/
│ │ │ ├── Element.java
│ │ │ ├── ConcreteElementA.java
│ │ │ ├── ConcreteElementB.java
│ │ │ ├── Visitor.java
│ │ │ ├── ConcreteVisitor1.java
│ │ │ ├── ConcreteVisitor2.java
│ │ │ ├── ObjectStructure.java
│ │ │ ├── ASTNode.java
│ │ │ ├── VariableNode.java
│ │ │ ├── AssignmentNode.java
│ │ │ ├── ASTVisitor.java
│ │ │ ├── TypeCheckVisitor.java
│ │ │ ├── CodeGenVisitor.java
│ │ │ ├── CompilerClient.java
│ │ │ ├── FileSystemElement.java
│ │ │ ├── File.java
│ │ │ ├── Directory.java
│ │ │ ├── FileSystemVisitor.java
│ │ │ ├── SizeCalculatorVisitor.java
│ │ │ └── FileSearchVisitor.java
相关推荐
LiLiYuan.2 分钟前
【Lombok库常用注解】
java·开发语言·python
Charles_go22 分钟前
C#中级45、什么是组合优于继承
开发语言·c#
培风图南以星河揽胜33 分钟前
Java实习模拟面试|离散数学|概率论|金融英语|数据库实战|职业规划|期末冲刺|今日本科计科要闻速递:技术分享与学习指南
java·面试·概率论
能鈺CMS34 分钟前
能鈺CMS · 虚拟发货源码
java·大数据·数据库
二川bro36 分钟前
数据可视化进阶:Python动态图表制作实战
开发语言·python·信息可视化
sheji341637 分钟前
【开题答辩全过程】以 环保监督管理系统为例,包含答辩的问题和答案
java·eclipse
不会玩电脑的Xin.40 分钟前
Web请求乱码解决方案
java·javaweb
Billow_lamb1 小时前
Spring Boot2.x.x 全局错误处理
java·spring boot·后端
编程火箭车1 小时前
【Java SE 基础学习打卡】14 Java 注释
java·编程规范·代码注释·api文档·代码可读性·javadoc·文档注释
雁于飞1 小时前
分布式基础
java·spring boot·分布式·spring·wpf·cloud native