Skip to content

访问者模式(Visitor Pattern)笔记

定义

访问者模式是一种行为设计模式,它允许你将算法与对象结构分离,使你能够在不修改现有对象结构的情况下定义新的操作。

英文原文定义

The Visitor Pattern represents an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates.

核心概念

访问者模式基于双重分派(double dispatch)原则,通过将操作逻辑从元素类中移出并放入独立的访问者类中,实现了数据结构和操作的分离。

主要角色

  1. Visitor(访问者接口):为每个ConcreteElement类声明visit操作
  2. ConcreteVisitor(具体访问者):实现Visitor声明的操作
  3. Element(元素接口):定义accept方法接受访问者
  4. ConcreteElement(具体元素):实现accept方法
  5. ObjectStructure(对象结构):能枚举它的元素,可提供高层接口允许访问者访问元素

优缺点

优点

  • 开闭原则:新增操作容易,无需修改元素类
  • 单一职责原则:相关行为集中在一个访问者中
  • 算法集中:将相关操作集中在一个访问者对象中
  • 跨类层次操作:可以跨多个类层次结构进行操作
  • 状态累积:访问者可以在遍历过程中累积状态

缺点

  • 破坏封装:需要元素类暴露足够多细节给访问者
  • 元素接口变更困难:增加新元素类需要修改所有访问者
  • 过度设计:对象结构很少变化时才适合使用
  • 访问权限:可能需要将元素内部状态公开

解决的问题

访问者模式主要解决以下经典问题:

  1. 操作与结构分离:需要对复杂对象结构(如组合结构)进行多种不相关操作时
  2. 避免污染类:当不想让这些操作"污染"元素的类时
  3. 跨类操作:当操作需要跨越多个类层次结构时
  4. 累积状态:需要在遍历过程中累积状态信息时
  5. 工具类膨胀:避免工具类因不断增加的操作而膨胀

实现注意事项

  1. 双重分派实现

    • 元素通过accept方法"反向调用"访问者的visit方法
    • 这是实现双重分派的关键机制
  2. 访问者状态

    • 无状态访问者:可以共享使用(单例)
    • 有状态访问者:每次需要新实例
  3. 元素接口稳定性

    • 元素类结构应相对稳定
    • 频繁添加新元素类会降低模式优势
  4. 循环依赖

    • 访问者知道所有元素类
    • 元素类知道访问者接口
    • 需要谨慎设计避免过度耦合
  5. 遍历责任

    • 对象结构可以处理遍历
    • 也可以由访问者控制遍历顺序

实现变体

  1. 内部访问者

    • 访问者作为元素类的内部类
    • 可以访问元素私有成员
  2. 默认访问者

    • 提供默认实现的抽象访问者类
    • 具体访问者只需覆盖需要的方法
  3. 扩展访问者

    • 通过组合多个访问者扩展功能
    • 而非创建庞大的访问者类
  4. 迭代访问者

    • 将访问者与迭代器结合
    • 支持复杂遍历逻辑

与其他模式的关系

相似模式区分

  1. 迭代器模式

    • 访问者:对元素执行操作
    • 迭代器:遍历集合元素
  2. 组合模式

    • 访问者:常用来对组合结构进行操作
    • 组合:描述部分-整体层次结构
  3. 策略模式

    • 访问者:封装多个操作,可跨类层次
    • 策略:封装单个算法

常见搭配组合

  1. 访问者 + 组合:对组合结构执行操作
  2. 访问者 + 解释器:在语法树上执行操作
  3. 访问者 + 迭代器:控制复杂遍历顺序
  4. 访问者 + 装饰器:动态增强访问者功能

典型应用场景

  • 编译器(语法树分析)
  • 文档处理(XML/HTML处理)
  • 复杂对象结构分析
  • 报表生成系统
  • 静态代码分析工具
  • 3D渲染场景遍历
  • 人工智能决策树处理

代码示例(编译器AST实现)

java
// 元素接口
interface ASTNode {
    void accept(ASTVisitor visitor);
}

// 具体元素:变量引用
class VariableRefNode implements ASTNode {
    private String variableName;
    
    public VariableRefNode(String name) {
        this.variableName = name;
    }
    
    public String getVariableName() {
        return variableName;
    }
    
    @Override
    public void accept(ASTVisitor visitor) {
        visitor.visit(this);
    }
}

// 具体元素:赋值语句
class AssignmentNode implements ASTNode {
    private VariableRefNode left;
    private ASTNode right;
    
    public AssignmentNode(VariableRefNode left, ASTNode right) {
        this.left = left;
        this.right = right;
    }
    
    public VariableRefNode getLeft() {
        return left;
    }
    
    public ASTNode getRight() {
        return right;
    }
    
    @Override
    public void accept(ASTVisitor visitor) {
        visitor.visit(this);
    }
}

// 访问者接口
interface ASTVisitor {
    void visit(VariableRefNode node);
    void visit(AssignmentNode node);
}

// 具体访问者:类型检查
class TypeCheckingVisitor implements ASTVisitor {
    @Override
    public void visit(VariableRefNode node) {
        System.out.println("Type checking variable reference: " + node.getVariableName());
        // 实际实现中会检查符号表等
    }
    
    @Override
    public void visit(AssignmentNode node) {
        System.out.println("Type checking assignment");
        node.getLeft().accept(this);
        node.getRight().accept(this);
        // 实际实现中会检查类型兼容性等
    }
}

// 具体访问者:代码生成
class CodeGenVisitor implements ASTVisitor {
    @Override
    public void visit(VariableRefNode node) {
        System.out.println("Generating code for variable: " + node.getVariableName());
        // 实际实现中会生成加载变量的指令
    }
    
    @Override
    public void visit(AssignmentNode node) {
        System.out.println("Generating assignment code");
        node.getRight().accept(this);
        node.getLeft().accept(this);
        // 实际实现中会生成存储指令
    }
}

// 对象结构:AST
class AST implements Iterable<ASTNode> {
    private List<ASTNode> nodes = new ArrayList<>();
    
    public void addNode(ASTNode node) {
        nodes.add(node);
    }
    
    @Override
    public Iterator<ASTNode> iterator() {
        return nodes.iterator();
    }
}

// 客户端代码
public class Compiler {
    public static void main(String[] args) {
        // 构建简单AST: a = b
        AST ast = new AST();
        VariableRefNode b = new VariableRefNode("b");
        VariableRefNode a = new VariableRefNode("a");
        AssignmentNode assign = new AssignmentNode(a, b);
        ast.addNode(assign);
        
        // 类型检查
        TypeCheckingVisitor typeChecker = new TypeCheckingVisitor();
        for (ASTNode node : ast) {
            node.accept(typeChecker);
        }
        
        // 代码生成
        CodeGenVisitor codeGen = new CodeGenVisitor();
        for (ASTNode node : ast) {
            node.accept(codeGen);
        }
    }
}