小易说IT 小易说IT

Java访问者模式(Visitor) 示例代码

访问者模式:把数据结构与数据操作分离,在不修改元素类的前提下,给元素增加新操作。核心角色:

  1. Element(元素接口):接收访问者 accept(Visitor)

  2. ConcreteElement:具体元素,实现 accept,调用 visitor.visit (this)

  3. Visitor(访问者接口):为每一种具体元素声明 visit 方法

  4. ConcreteVisitor:具体访问者,实现对每个元素的业务逻辑

  5. ObjectStructure:对象结构,持有元素集合,提供遍历

import java.util.ArrayList;
import java.util.List;

// ---------------------- 1. 访问者接口 Visitor ----------------------
interface Visitor {
    void visit(Engineer engineer);
    void visit(Manager manager);
}

// ---------------------- 2. 元素接口 Element ----------------------
interface Employee {
    // 接收访问者
    void accept(Visitor visitor);
}

// ---------------------- 3. 具体元素:工程师 Engineer ----------------------
class Engineer implements Employee {
    private String name;
    private int workHours;

    public Engineer(String name, int workHours) {
        this.name = name;
        this.workHours = workHours;
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }

    public String getName() {
        return name;
    }

    public int getWorkHours() {
        return workHours;
    }
}

// ---------------------- 4. 具体元素:经理 Manager ----------------------
class Manager implements Employee {
    private String name;
    private int productCount;

    public Manager(String name, int productCount) {
        this.name = name;
        this.productCount = productCount;
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }

    public String getName() {
        return name;
    }

    public int getProductCount() {
        return productCount;
    }
}

// ---------------------- 5. 具体访问者1:薪资计算访问者 ----------------------
class SalaryVisitor implements Visitor {
    @Override
    public void visit(Engineer engineer) {
        // 工程师:工时 * 150
        double salary = engineer.getWorkHours() * 150;
        System.out.printf("工程师【%s】薪资:%.2f 元%n", engineer.getName(), salary);
    }

    @Override
    public void visit(Manager manager) {
        // 经理:项目数 * 5000
        double salary = manager.getProductCount() * 5000;
        System.out.printf("经理【%s】薪资:%.2f 元%n", manager.getName(), salary);
    }
}

// ---------------------- 6. 具体访问者2:KPI考核访问者(新增操作,不用改Employee) ----------------------
class KpiVisitor implements Visitor {
    @Override
    public void visit(Engineer engineer) {
        String kpi = engineer.getWorkHours() > 160 ? "优秀" : "合格";
        System.out.printf("工程师【%s】KPI评价:%s%n", engineer.getName(), kpi);
    }

    @Override
    public void visit(Manager manager) {
        String kpi = manager.getProductCount() >= 3 ? "优秀" : "合格";
        System.out.printf("经理【%s】KPI评价:%s%n", manager.getName(), kpi);
    }
}

// ---------------------- 7. 对象结构:员工集合,提供遍历 ----------------------
class EmployeeStructure {
    private final List<Employee> employeeList = new ArrayList<>();

    public void add(Employee employee) {
        employeeList.add(employee);
    }

    // 让所有元素接受访问者
    public void accept(Visitor visitor) {
        for (Employee emp : employeeList) {
            emp.accept(visitor);
        }
    }
}

// ---------------------- 测试客户端 ----------------------
public class VisitorDemo {
    public static void main(String[] args) {
        EmployeeStructure structure = new EmployeeStructure();
        structure.add(new Engineer("张三", 180));
        structure.add(new Engineer("李四", 140));
        structure.add(new Manager("王总", 4));
        structure.add(new Manager("赵总", 2));

        System.out.println("===== 计算薪资 =====");
        structure.accept(new SalaryVisitor());

        System.out.println("\n===== KPI考核 =====");
        structure.accept(new KpiVisitor());
    }
}

输出结果

===== 计算薪资 =====
工程师【张三】薪资:27000.00 元
工程师【李四】薪资:21000.00 元
经理【王总】薪资:20000.00 元
经理【赵总】薪资:10000.00 元

===== KPI考核 =====
工程师【张三】KPI评价:优秀
工程师【李四】KPI评价:合格
经理【王总】KPI评价:优秀
经理【赵总】KPI评价:合格

模式要点

优点

  1. 新增操作只新增 Visitor 实现,不需要修改元素类,符合开闭原则

  2. 相关业务逻辑收拢在 Visitor 类中

缺点

  1. 如果要新增一个Employee子类(比如HR),Visitor 接口和所有访问者实现都要增加visit(HR),改动很大

适用场景元素类固定很少变化,但操作经常扩展;比如 AST 语法树、文件目录遍历、报表统计。

调用流程

  1. ObjectStructure 遍历集合调用element.accept(visitor)

  2. ConcreteElement.accept 内部执行 visitor.visit(this),触发访问者对应重载方法

  3. Visitor 拿到具体元素对象,执行业务逻辑


本文原创作者:易君召,详见:https://www.yijunzhao.cc/about,转载请注明出处。

原文链接 https://yijunzhao.cc/archives/java-visitor-pattern-example-code

欢迎访问 https://www.yijunzhao.cc/

https://www.yijunzhao.cc/