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
相关推荐
JAVA面经实录9176 小时前
Java企业级工程化·终极完整版背诵手册(无遗漏、全覆盖、面试+落地通用)
java·开发语言·面试
周杰伦fans7 小时前
AutoCAD .NET 二次开发:深入理解 EntityJig 的工作原理与正确实现
开发语言·.net
许彰午8 小时前
CacheSQL(二):主从复制——OpLog 环形缓冲区与故障自动恢复
java·数据库·缓存
Bat U9 小时前
JavaEE|多线程初阶(七)
java·开发语言
谭欣辰9 小时前
C++ 排列组合完整指南
开发语言·c++·算法
foundbug99910 小时前
自适应滤除直达波干扰的MATLAB实现
开发语言·算法·matlab
XDH_CS10 小时前
MySQL 8.0 安装与 MySQL Workbench 使用全流程(超详细教程)
开发语言·数据库·mysql
小短腿的代码世界11 小时前
Qt实时盈亏计算深度解析:从持仓数据到动态盈亏展示
开发语言·qt
小康小小涵11 小时前
基于ESP32S3实现无人机RID模块底层源码编译
linux·开发语言·python