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
相关推荐
kkai人工智能10 分钟前
解决开发者技能差距:AI 在提升效率与技能培养中的作用
开发语言·人工智能·ai·chatgpt·媒体
啊阿狸不会拉杆12 分钟前
《软件工程》实战— 在线教育平台开发
java·vue.js·软件工程·团队开发
移动开发者1号17 分钟前
Fragment事务commit与commitNow区别
android·java
赴前尘20 分钟前
Go 中 `json.NewEncoder/Decoder` 与 `json.Marshal/Unmarshal` 的区别与实践
开发语言·golang·json
꧁༺摩༒西༻꧂24 分钟前
Python生成日历导出Excel
java·前端·python
gou1234123425 分钟前
【Golang入门】第一章:环境搭建与Hello World
开发语言·后端·golang
铭....1 小时前
word批量导出visio图
开发语言·c#·word
在未来等你1 小时前
互联网大厂Java求职面试:AI大模型融合下的企业知识库架构设计与性能优化
java·向量数据库·ai大模型·spring ai·语义缓存·rag系统·多模态处理
寻星探路1 小时前
JAVA与C语言之间的差异(一)
java·开发语言
tqs_123451 小时前
IntelliJ IDEA 中进行背景设置
java·ide·intellij-idea