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
相关推荐
zx132310 分钟前
chrome提示https不安全, 不能记住账号密码怎么办? 可以利用js输入账号
开发语言·javascript·ecmascript
你是狒狒吗13 分钟前
HttpServletRequest是什么
java
你们补药再卷啦26 分钟前
springboot 项目 jmeter简单测试流程
java·spring boot·后端
巴巴_羊33 分钟前
React Redux
开发语言·前端·javascript
菜鸡且互啄6937 分钟前
sql 向Java的映射
java·开发语言
顾林海1 小时前
深度解析HashMap工作原理
android·java·面试
雷渊1 小时前
介绍一下RocketMQ的几种集群模式
java·后端·面试
菜菜的后端私房菜1 小时前
深入解析Java SPI🌟从使用到原理的全面之旅🚀
java·后端·设计
AronTing1 小时前
01-Spring Cloud Alibaba 微服务架构总览与核心组件详解
java·微服务·架构
waterHBO1 小时前
python 微博爬虫 01
开发语言·爬虫·python