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
相关推荐
1点东西8 分钟前
新来的同事问我当进程/机器突然停止时,finally 到底会不会执行?
java·后端·程序员
Aspartame~38 分钟前
K8s的相关知识总结
java·容器·kubernetes
寒士obj1 小时前
MyBatis-Plus基础篇详解
java·mybatis
我崽不熬夜1 小时前
List、Set、Map,你真的会选用吗?
java·后端·java ee
我爱学嵌入式1 小时前
C语言:第18天笔记
c语言·开发语言·笔记
Y4090012 小时前
Java算法之排序
java·数据结构·笔记·算法
楚Y6同学2 小时前
QT之QSS的使用方法和常用控件的样式设置
开发语言·qt
ALLSectorSorft2 小时前
搭子交友 app 动态分享与打卡系统设计实现
java·服务器·数据库·人工智能·oracle·交友
long3162 小时前
适配器模式 java demo
java·javascript·后端·程序人生·设计模式·适配器模式
##学无止境##2 小时前
从青铜到王者:Java设计模式——代理模式
java·设计模式·代理模式