访问者模式

简介

表示一个作用于某对象结构中的各元素的操作,可以在不改变各元素类的前提下定义新操作。

使用场景:

  • 编译器的AST遍历

  • XML/DOM树操作

  • 报表生成

  • 文件系统遍历

实现要点:

  • 双分派机制

  • 访问者为每种元素定义visit()方法

  • 元素实现accept()方法

UML

代码示例

  1. 访问者接口

public interface Visitor {
    
    /**
     * 访问工程师
     */
    void visit(Engineer engineer);
    
    /**
     * 访问经理
     */
    void visit(Manager manager);
    
    /**
     * 访问CEO
     */
    void visit(CEO ceo);
}
  1. 访问者实现

/**
 * @author dcx
 * @description 年度报告生成器访问者 - 具体访问者
 * @create 2025-01-27
 */
public class AnnualReportGenerator implements Visitor {
    
    private StringBuilder report;
    
    public AnnualReportGenerator() {
        report = new StringBuilder();
        report.append("📊 ========== 年度报告 ==========\n");
    }
    
    @Override
    public void visit(Engineer engineer) {
        report.append("\n👨‍💻 工程师: ").append(engineer.getName()).append("\n");
        report.append("   职位: ").append(engineer.getPosition()).append("\n");
        report.append("   工作年限: ").append(engineer.getWorkingYears()).append("年\n");
        report.append("   年度代码量: ").append(engineer.getCodeLines()).append("行\n");
        report.append("   工作评价: 代码质量优秀,技术能力强\n");
        
        System.out.println("✅ 已为 " + engineer.getName() + " 生成年度报告");
    }
    
    @Override
    public void visit(Manager manager) {
        report.append("\n👔 经理: ").append(manager.getName()).append("\n");
        report.append("   职位: ").append(manager.getPosition()).append("\n");
        report.append("   工作年限: ").append(manager.getWorkingYears()).append("年\n");
        report.append("   团队规模: ").append(manager.getTeamSize()).append("人\n");
        report.append("   完成项目: ").append(manager.getProjectCount()).append("个\n");
        report.append("   工作评价: 管理能力出色,团队协作良好\n");
        
        System.out.println("✅ 已为 " + manager.getName() + " 生成年度报告");
    }
    
    @Override
    public void visit(CEO ceo) {
        report.append("\n👨‍💼 CEO: ").append(ceo.getName()).append("\n");
        report.append("   职位: ").append(ceo.getPosition()).append("\n");
        report.append("   工作年限: ").append(ceo.getWorkingYears()).append("年\n");
        report.append("   管理部门: ").append(ceo.getDepartmentCount()).append("个\n");
        report.append("   公司营收: ¥").append(String.format("%.2f", ceo.getCompanyRevenue())).append("万\n");
        report.append("   工作评价: 战略眼光卓越,领导力强\n");
        
        System.out.println("✅ 已为 " + ceo.getName() + " 生成年度报告");
    }
    
    /**
     * 获取报告内容
     */
    public String getReport() {
        report.append("\n========== 报告结束 ==========\n");
        return report.toString();
    }
}
/**
 * @author dcx
 * @description 绩效评估访问者 - 具体访问者
 * @create 2025-01-27
 */
public class PerformanceEvaluator implements Visitor {
    
    private int totalScore = 0;
    private int count = 0;
    
    @Override
    public void visit(Engineer engineer) {
        // 工程师绩效评分 = 工作年限分 + 代码质量分
        int score = engineer.getWorkingYears() * 10 + (engineer.getCodeLines() / 10000) * 20;
        if (score > 100) score = 100;
        
        totalScore += score;
        count++;
        
        System.out.println("👨‍💻 工程师 " + engineer.getName() + " 的绩效评分: " + score + "分");
        System.out.println("   评级: " + getGrade(score));
    }
    
    @Override
    public void visit(Manager manager) {
        // 经理绩效评分 = 工作年限分 + 团队规模分 + 项目完成分
        int score = manager.getWorkingYears() * 8 + 
                   manager.getTeamSize() * 5 + 
                   manager.getProjectCount() * 10;
        if (score > 100) score = 100;
        
        totalScore += score;
        count++;
        
        System.out.println("👔 经理 " + manager.getName() + " 的绩效评分: " + score + "分");
        System.out.println("   评级: " + getGrade(score));
    }
    
    @Override
    public void visit(CEO ceo) {
        // CEO绩效评分 = 工作年限分 + 部门管理分 + 公司营收分
        int score = ceo.getWorkingYears() * 5 + 
                   ceo.getDepartmentCount() * 8 + 
                   (int)(ceo.getCompanyRevenue() / 10000) * 30;
        if (score > 100) score = 100;
        
        totalScore += score;
        count++;
        
        System.out.println("👨‍💼 CEO " + ceo.getName() + " 的绩效评分: " + score + "分");
        System.out.println("   评级: " + getGrade(score));
    }
    
    /**
     * 根据分数获取评级
     */
    private String getGrade(int score) {
        if (score >= 90) return "S (优秀)";
        if (score >= 80) return "A (良好)";
        if (score >= 70) return "B (中等)";
        if (score >= 60) return "C (及格)";
        return "D (不及格)";
    }
    
    /**
     * 获取平均绩效
     */
    public double getAverageScore() {
        return count == 0 ? 0 : (double) totalScore / count;
    }
    
    /**
     * 重置统计
     */
    public void reset() {
        totalScore = 0;
        count = 0;
    }
}
/**
 * @author dcx
 * @description 薪资计算访问者 - 具体访问者
 * @create 2025-01-27
 */
public class SalaryCalculator implements Visitor {
    
    private double totalSalary = 0;
    
    @Override
    public void visit(Engineer engineer) {
        // 工程师薪资 = 基本工资 + 工作年限奖金 + 代码行数奖金
        double salary = 10000 + engineer.getWorkingYears() * 1000 + engineer.getCodeLines() / 1000.0 * 500;
        totalSalary += salary;
        
        System.out.println("👨‍💻 工程师 " + engineer.getName() + " 的薪资: ¥" + String.format("%.2f", salary));
        System.out.println("   工作年限: " + engineer.getWorkingYears() + "年");
        System.out.println("   代码行数: " + engineer.getCodeLines() + "行");
    }
    
    @Override
    public void visit(Manager manager) {
        // 经理薪资 = 基本工资 + 工作年限奖金 + 团队规模奖金 + 项目数量奖金
        double salary = 20000 + manager.getWorkingYears() * 2000 + 
                       manager.getTeamSize() * 500 + manager.getProjectCount() * 1000;
        totalSalary += salary;
        
        System.out.println("👔 经理 " + manager.getName() + " 的薪资: ¥" + String.format("%.2f", salary));
        System.out.println("   工作年限: " + manager.getWorkingYears() + "年");
        System.out.println("   团队规模: " + manager.getTeamSize() + "人");
        System.out.println("   项目数量: " + manager.getProjectCount() + "个");
    }
    
    @Override
    public void visit(CEO ceo) {
        // CEO薪资 = 基本工资 + 工作年限奖金 + 部门数量奖金 + 公司营收提成
        double salary = 50000 + ceo.getWorkingYears() * 5000 + 
                       ceo.getDepartmentCount() * 2000 + ceo.getCompanyRevenue() * 0.01;
        totalSalary += salary;
        
        System.out.println("👨‍💼 CEO " + ceo.getName() + " 的薪资: ¥" + String.format("%.2f", salary));
        System.out.println("   工作年限: " + ceo.getWorkingYears() + "年");
        System.out.println("   部门数量: " + ceo.getDepartmentCount() + "个");
        System.out.println("   公司营收: ¥" + String.format("%.2f", ceo.getCompanyRevenue()) + "万");
    }
    
    /**
     * 获取总薪资
     */
    public double getTotalSalary() {
        return totalSalary;
    }
    
    /**
     * 重置总薪资
     */
    public void reset() {
        totalSalary = 0;
    }
}
  1. 员工接口

public interface Employee {
    
    /**
     * 接受访问者
     */
    void accept(Visitor visitor);
    
    /**
     * 获取员工姓名
     */
    String getName();
    
    /**
     * 获取员工职位
     */
    String getPosition();
}
  1. 具体员工

/**
 * @author dcx
 * @description CEO类 - 具体元素
 * @create 2025-01-27
 */
public class CEO implements Employee {
    
    private String name;
    private int workingYears;
    private int departmentCount;
    private double companyRevenue;
    
    public CEO(String name, int workingYears, int departmentCount, double companyRevenue) {
        this.name = name;
        this.workingYears = workingYears;
        this.departmentCount = departmentCount;
        this.companyRevenue = companyRevenue;
    }
    
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
    
    @Override
    public String getName() {
        return name;
    }
    
    @Override
    public String getPosition() {
        return "CEO";
    }
    
    public int getWorkingYears() {
        return workingYears;
    }
    
    public int getDepartmentCount() {
        return departmentCount;
    }
    
    public double getCompanyRevenue() {
        return companyRevenue;
    }
}
/**
 * @author dcx
 * @description 工程师类 - 具体元素
 * @create 2025-01-27
 */
public class Engineer implements Employee {
    
    private String name;
    private int workingYears;
    private int codeLines;
    
    public Engineer(String name, int workingYears, int codeLines) {
        this.name = name;
        this.workingYears = workingYears;
        this.codeLines = codeLines;
    }
    
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
    
    @Override
    public String getName() {
        return name;
    }
    
    @Override
    public String getPosition() {
        return "工程师";
    }
    
    public int getWorkingYears() {
        return workingYears;
    }
    
    public int getCodeLines() {
        return codeLines;
    }
}
/**
 * @author dcx
 * @description 经理类 - 具体元素
 * @create 2025-01-27
 */
public class Manager implements Employee {
    
    private String name;
    private int workingYears;
    private int teamSize;
    private int projectCount;
    
    public Manager(String name, int workingYears, int teamSize, int projectCount) {
        this.name = name;
        this.workingYears = workingYears;
        this.teamSize = teamSize;
        this.projectCount = projectCount;
    }
    
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
    
    @Override
    public String getName() {
        return name;
    }
    
    @Override
    public String getPosition() {
        return "经理";
    }
    
    public int getWorkingYears() {
        return workingYears;
    }
    
    public int getTeamSize() {
        return teamSize;
    }
    
    public int getProjectCount() {
        return projectCount;
    }
}
  1. 公司类

public class Company {
    
    private String name;
    private List<Employee> employees;
    
    public Company(String name) {
        this.name = name;
        this.employees = new ArrayList<>();
    }
    
    /**
     * 添加员工
     */
    public void addEmployee(Employee employee) {
        employees.add(employee);
        System.out.println("➕ 添加员工: " + employee.getName() + " (" + employee.getPosition() + ")");
    }
    
    /**
     * 移除员工
     */
    public void removeEmployee(Employee employee) {
        employees.remove(employee);
        System.out.println("➖ 移除员工: " + employee.getName());
    }
    
    /**
     * 接受访问者访问所有员工
     */
    public void accept(Visitor visitor) {
        System.out.println("\n🔍 访问者 [" + visitor.getClass().getSimpleName() + "] 正在访问公司员工...\n");
        for (Employee employee : employees) {
            employee.accept(visitor);
        }
    }
    
    /**
     * 获取员工数量
     */
    public int getEmployeeCount() {
        return employees.size();
    }
    
    /**
     * 获取公司名称
     */
    public String getName() {
        return name;
    }
    
    /**
     * 显示公司信息
     */
    public void showCompanyInfo() {
        System.out.println("\n🏢 公司: " + name);
        System.out.println("👥 员工数量: " + employees.size());
        System.out.println("员工列表:");
        for (Employee employee : employees) {
            System.out.println("  - " + employee.getName() + " (" + employee.getPosition() + ")");
        }
    }
}
  1. 使用方式

public class Test {

    public static void main(String[] args) {
        System.out.println("=== 访问者模式 - 公司员工管理示例 ===\n");

        // 创建公司(对象结构)
        Company company = new Company("科技创新公司");

        System.out.println("【初始化公司员工】\n");
        
        // 创建员工(元素)
        Employee engineer1 = new Engineer("张三", 3, 50000);
        Employee engineer2 = new Engineer("李四", 5, 80000);
        Employee manager1 = new Manager("王五", 8, 10, 15);
        Employee manager2 = new Manager("赵六", 6, 8, 12);
        Employee ceo = new CEO("刘总", 15, 5, 50000);

        // 添加员工到公司
        company.addEmployee(engineer1);
        company.addEmployee(engineer2);
        company.addEmployee(manager1);
        company.addEmployee(manager2);
        company.addEmployee(ceo);

        // 显示公司信息
        company.showCompanyInfo();

        // ========== 场景1:薪资计算 ==========
        System.out.println("\n\n【场景1:薪资计算访问者】");
        
        SalaryCalculator salaryCalculator = new SalaryCalculator();
        company.accept(salaryCalculator);
        
        System.out.println("\n💰 公司总薪资: ¥" + String.format("%.2f", salaryCalculator.getTotalSalary()));
        System.out.println("💰 平均薪资: ¥" + String.format("%.2f", salaryCalculator.getTotalSalary() / company.getEmployeeCount()));

        // ========== 场景2:绩效评估 ==========
        System.out.println("\n\n【场景2:绩效评估访问者】");
        
        PerformanceEvaluator performanceEvaluator = new PerformanceEvaluator();
        company.accept(performanceEvaluator);
        
        System.out.println("\n📈 平均绩效: " + String.format("%.1f", performanceEvaluator.getAverageScore()) + "分");

        // ========== 场景3:年度报告生成 ==========
        System.out.println("\n\n【场景3:年度报告生成访问者】");
        
        AnnualReportGenerator reportGenerator = new AnnualReportGenerator();
        company.accept(reportGenerator);
        
        System.out.println("\n📄 年度报告内容:");
        System.out.println(reportGenerator.getReport());

        // ========== 场景4:添加新的访问者 ==========
        System.out.println("\n【场景4:演示添加新访问者的便利性】");
        System.out.println("假设现在需要添加新功能:统计员工信息");
        System.out.println("✅ 只需要创建一个新的Visitor实现类");
        System.out.println("❌ 不需要修改任何Employee类");
        System.out.println("这就是访问者模式的优势!");

        // ========== 对比不使用访问者模式 ==========
        System.out.println("\n\n【对比:不使用访问者模式】\n");
        
        System.out.println("❌ 不使用访问者模式:");
        System.out.println("  - 需要在每个员工类中添加计算薪资、评估绩效等方法");
        System.out.println("  - 添加新操作时,需要修改所有员工类");
        System.out.println("  - 违反开闭原则,维护成本高");
        System.out.println("  - 代码分散,难以统一管理");
        System.out.println("\n  示例代码:");
        System.out.println("  class Engineer {");
        System.out.println("      double calculateSalary() { ... }");
        System.out.println("      int evaluatePerformance() { ... }");
        System.out.println("      String generateReport() { ... }");
        System.out.println("      // 每次新增功能都要修改这里");
        System.out.println("  }");
        
        System.out.println("\n✅ 使用访问者模式:");
        System.out.println("  - 员工类保持稳定,只需实现accept()方法");
        System.out.println("  - 添加新操作只需创建新的Visitor");
        System.out.println("  - 符合开闭原则,易于扩展");
        System.out.println("  - 相关操作集中在访问者中");
        System.out.println("\n  示例代码:");
        System.out.println("  class Engineer {");
        System.out.println("      void accept(Visitor visitor) {");
        System.out.println("          visitor.visit(this);");
        System.out.println("      }");
        System.out.println("      // 添加新功能不需要修改这里");
        System.out.println("  }");

        // ========== 总结 ==========
        System.out.println("\n\n=== 访问者模式说明 ===");
        System.out.println("1. 访问者接口: Visitor - 为每种元素定义访问方法");
        System.out.println("2. 具体访问者: SalaryCalculator等 - 实现具体的访问操作");
        System.out.println("3. 元素接口: Employee - 定义accept()方法");
        System.out.println("4. 具体元素: Engineer、Manager、CEO - 实现accept()");
        System.out.println("5. 对象结构: Company - 存储元素集合");

        System.out.println("\n=== 访问者模式优势 ===");
        System.out.println("✅ 符合开闭原则: 添加新操作无需修改元素类");
        System.out.println("✅ 相关操作集中: 同一操作的代码集中在一起");
        System.out.println("✅ 易于添加新操作: 只需创建新的访问者");
        System.out.println("✅ 数据结构稳定: 元素类结构保持稳定");
        System.out.println("✅ 灵活性强: 可以为不同元素定义不同操作");

        System.out.println("\n=== 访问者模式缺点 ===");
        System.out.println("⚠️ 难以添加新元素: 需要修改所有访问者");
        System.out.println("⚠️ 破坏封装: 访问者需要访问元素的内部数据");
        System.out.println("⚠️ 依赖具体类: 访问者依赖具体的元素类");

        System.out.println("\n=== 访问者模式应用场景 ===");
        System.out.println("📌 编译器: AST遍历、代码生成、语义检查");
        System.out.println("📌 文档处理: XML/DOM树的遍历和操作");
        System.out.println("📌 报表生成: 不同格式的报表生成");
        System.out.println("📌 对象持久化: 将对象保存为不同格式");
        System.out.println("📌 数据统计: 对集合进行各种统计操作");
        System.out.println("📌 权限检查: 对不同类型对象进行权限验证");

        System.out.println("\n=== 访问者模式关键点 ===");
        System.out.println("🔑 双分派: 运行时根据两个对象类型确定方法");
        System.out.println("🔑 元素稳定: 元素类结构不经常变化");
        System.out.println("🔑 操作多变: 经常需要添加新的操作");
        System.out.println("🔑 accept()方法: 每个元素都要实现");

        System.out.println("\n=== 双分派机制 ===");
        System.out.println("第一次分派: employee.accept(visitor)");
        System.out.println("第二次分派: visitor.visit(this)");
        System.out.println("结果: 运行时确定具体的visit(Engineer)方法");

        System.out.println("\n=== 何时使用访问者模式 ===");
        System.out.println("✅ 对象结构稳定,不经常添加新元素");
        System.out.println("✅ 经常需要对对象结构中的元素进行新操作");
        System.out.println("✅ 需要对不同类型的元素执行不同操作");
        System.out.println("✅ 对象结构包含多种类型的对象");

        System.out.println("\n=== 何时不使用访问者模式 ===");
        System.out.println("❌ 对象结构经常变化(添加新元素)");
        System.out.println("❌ 元素类需要保持高度封装");
        System.out.println("❌ 操作类型固定,不需要经常添加");

        System.out.println("\n=== Java中的访问者模式应用 ===");
        System.out.println("🔸 javax.lang.model.element.Element: Java编译器API");
        System.out.println("🔸 org.w3c.dom.Node: DOM树遍历");
        System.out.println("🔸 ASM/ByteBuddy: 字节码操作");
        System.out.println("🔸 文件系统: java.nio.file.FileVisitor");
    }
}

Last updated

Was this helpful?