rokevin
移动
前端
语言
  • 基础

    • Linux
    • 实施
    • 版本构建
  • 应用

    • WEB服务器
    • 数据库
  • 资讯

    • 工具
    • 部署
开放平台
产品设计
  • 人工智能
  • 云计算
计算机
其它
GitHub
移动
前端
语言
  • 基础

    • Linux
    • 实施
    • 版本构建
  • 应用

    • WEB服务器
    • 数据库
  • 资讯

    • 工具
    • 部署
开放平台
产品设计
  • 人工智能
  • 云计算
计算机
其它
GitHub
  • 访问者模式(Visitor Pattern)

  • 定义
  • 意图
  • 结构
  • 优点
  • 缺点
  • 类图(Mermaid)
  • 时序图(Mermaid)
  • 适用环境
  • 模式分析
    • 1. 双分派机制(Double Dispatch)
    • 2. 分离的核心价值
  • 模式扩展
    • 1. 组合访问者(Composite Visitor)
    • 2. 带状态的访问者(Stateful Visitor)
    • 3. 过滤访问者(Filtered Visitor)
  • 模式应用
  • Android 中的应用
    • 1. 注解处理器(APT)
    • 2. 资源解析
    • 3. RecyclerView 遍历(自定义场景)
    • 4. 数据库操作(Room)
  • 代码实现(Java 版)
    • 1. 抽象访问者(Visitor)
    • 2. 具体访问者(ConcreteVisitor)
    • 3. 抽象元素(Element)
    • 4. 具体元素(ConcreteElement)
    • 5. 对象结构(ObjectStructure)
    • 6. 客户端(Client)
    • 7. 运行结果
    • 实现2
  • 总结

访问者模式(Visitor Pattern)

当你想要为一个对象的组合增加新的能力, 且封装并不重要时, 就使用访问者模式。

访问者模式是行为型设计模式的一种,其核心思想是分离数据结构与数据操作,使得操作可以独立于数据结构进行扩展。在面对 “数据结构稳定但操作多变” 的场景时,访问者模式能显著提升代码的灵活性和可维护性。

定义

访问者模式的官方定义为: 表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素类的前提下,定义作用于这些元素的新操作。

简单来说:假设存在一个 “对象结构”(如集合、树形结构),其中包含多个 “元素”(Element)。访问者(Visitor)可以遍历该结构,并对不同类型的元素执行自定义操作,且新增操作时无需修改元素类或对象结构。

意图

访问者模式的核心意图是解决以下问题:

  1. 当数据结构(元素集合)稳定不变,但需要对元素执行多种不同且易扩展的操作时,避免在元素类中堆砌大量操作方法(导致类臃肿、违反单一职责)。
  2. 让操作(访问者)可以独立于元素类进行扩展,新增操作只需新增访问者,无需修改现有代码(符合开闭原则)。
  3. 集中管理同一类操作(如所有 “统计类” 操作放在统计访问者中,所有 “报表类” 操作放在报表访问者中),提升代码可读性和可维护性。

结构

访问者模式包含 5 个核心角色,各角色职责明确,协同实现 “数据结构与操作分离”:

角色名称核心职责
抽象访问者(Visitor)定义访问所有具体元素的接口,声明一个或多个visit(具体元素)方法(对应每种元素类型)。
具体访问者(ConcreteVisitor)实现抽象访问者的接口,定义对具体元素的实际操作逻辑(如统计、计算、打印等)。
抽象元素(Element)定义接受访问者的接口,声明accept(Visitor visitor)方法,该方法会调用访问者的visit方法。
具体元素(ConcreteElement)实现抽象元素的accept方法,在方法内部调用访问者的visit(当前元素)(触发双分派)。
对象结构(ObjectStructure)存储所有元素的集合,提供遍历元素的方法,并允许访问者访问其内部元素(如accept(Visitor))。

优点

  1. 符合开闭原则:新增操作只需新增具体访问者,无需修改元素类或对象结构。
  2. 集中管理操作:同一类操作(如 “统计类”“报表类”)可集中在一个访问者中,避免元素类臃肿(符合单一职责)。
  3. 灵活扩展操作:可快速切换不同访问者,实现对同一元素集合的不同操作(如 HR 评估、财务计算、行政登记)。
  4. 简化元素类:元素类只需关注自身属性和accept方法,无需包含大量操作逻辑。

缺点

  1. 破坏元素封装性:访问者需获取元素的内部状态(如薪资、时薪),元素需提供Getter或暴露私有属性,违反封装原则。
  2. 元素类变更困难:若新增 / 删除具体元素(如新增 “实习生”),需修改所有访问者的接口和实现(添加visit(Intern)方法),违反开闭原则。
  3. 依赖关系复杂:访问者与元素之间存在强耦合(访问者需知道所有元素类型),增加代码理解和维护成本。
  4. 对象结构遍历依赖:对象结构需提供遍历接口,若结构复杂(如树形结构),遍历逻辑可能与访问者耦合。

类图(Mermaid)

- Visitor: 访问者,为每一个 ConcreteElement 声明一个 visit 操作

- ConcreteVisitor: 具体访问者,存储遍历过程中的累计结果

- ObjectStructure: 对象结构,可以是组合结构,或者是一个集合。

以 “公司部门访问员工” 为例(HR 部门评估绩效、财务部门计算薪资),类图如下:

+---------------------+       +---------------------+
|     Visitor         |       |     Element         |
+---------------------+       +---------------------+
| + visit(FullTime):void |<--->| + accept(Visitor):void |
| + visit(PartTime):void |       +---------------------+
+---------------------+              ^
        ^                            |
        |                            |
+---------------------+       +---------------------+
| HRDepartment        |       | FullTimeEmployee    |
+---------------------+       +---------------------+
| + visit(FullTime):void |       | - name:String       |
| + visit(PartTime):void |       | - salary:int        |
+---------------------+       | + accept(Visitor):void |
                              +---------------------+
+---------------------+              ^
| FinanceDepartment   |              |
+---------------------+       +---------------------+
| + visit(FullTime):void |       | PartTimeEmployee    |
| + visit(PartTime):void |       +---------------------+
+---------------------+       | - name:String       |
                              | - hourlyWage:int     |
                              | + accept(Visitor):void |
                              +---------------------+
                                        ^
                                        |
                              +---------------------+
                              | EmployeeList        |  // 对象结构
                              +---------------------+
                              | - employees:List<Element> |
                              | + add(Element):void      |
                              | + accept(Visitor):void   |
                              +---------------------+
classDiagram
    direction TB

    %% 1. 抽象访问者(Visitor):定义访问所有具体元素的接口
    class Department {
        <<interface>>
        + visit(FullTimeEmployee employee) void
        + visit(PartTimeEmployee employee) void
    }

    %% 2. 具体访问者(ConcreteVisitor):实现抽象访问者的操作逻辑
    class HRDepartment {
        + visit(FullTimeEmployee employee) void
        + visit(PartTimeEmployee employee) void
    }
    class FinanceDepartment {
        + visit(FullTimeEmployee employee) void
        + visit(PartTimeEmployee employee) void
    }

    %% 3. 抽象元素(Element):定义接受访问者的接口
    class Employee {
        <<interface>>
        + accept(Department department) void
        + getName() String
    }

    %% 4. 具体元素(ConcreteElement):实现 accept 方法,触发访问者操作
    class FullTimeEmployee {
        - name: String
        - salary: int  // 月薪
        + FullTimeEmployee(name: String, salary: int)
        + accept(Department department) void
        + getName() String
        + getSalary() int
    }
    class PartTimeEmployee {
        - name: String
        - hourlyWage: int  // 时薪
        + PartTimeEmployee(name: String, hourlyWage: int)
        + accept(Department department) void
        + getName() String
        + getHourlyWage() int
    }

    %% 5. 对象结构(ObjectStructure):管理元素集合,提供遍历入口
    class EmployeeList {
        - employees: List~Employee~
        + addEmployee(employee: Employee) void
        + accept(department: Department) void
    }

    %% 角色间依赖关系
    %% 访问者与元素:具体访问者依赖具体元素(通过 visit 方法参数)
    HRDepartment ..|> Department  // 具体访问者实现抽象访问者
    FinanceDepartment ..|> Department

    %% 元素与访问者:具体元素依赖抽象访问者(通过 accept 方法参数)
    FullTimeEmployee ..|> Employee  // 具体元素实现抽象元素
    PartTimeEmployee ..|> Employee

    %% 对象结构与元素:对象结构聚合元素(内部存储 Employee 列表)
    EmployeeList "1" -- "*" Employee : 包含 >

    %% 双分派核心依赖:元素调用访问者的 visit 方法,访问者操作元素属性
    FullTimeEmployee ..> Department : 调用 accept >
    PartTimeEmployee ..> Department : 调用 accept >
    Department ..> FullTimeEmployee : 调用 visit >
    Department ..> PartTimeEmployee : 调用 visit >

时序图(Mermaid)

延续 “HR 部门访问员工” 场景,时序图展示交互流程:

  1. 客户端(Client)创建对象结构(EmployeeList)、具体元素(FullTimeEmployee/PartTimeEmployee)和具体访问者(HRDepartment)。
  2. 客户端将元素添加到对象结构中。
  3. 客户端调用对象结构的accept(HRDepartment)方法。
  4. 对象结构遍历内部元素,调用每个元素的accept(HRDepartment)方法。
  5. 元素调用访问者的visit(当前元素)方法,触发具体操作。
sequenceDiagram
    participant Client as 客户端
    participant EmpList as 对象结构(EmployeeList)
    participant FullTime as 具体元素(FullTimeEmployee)
    participant HR as 具体访问者(HRDepartment)

    %% 1. 初始化对象结构
    Client->>EmpList: 1. new EmployeeList()
    Note right of Client: 创建存储员工的集合结构

    %% 2. 初始化具体元素(全职员工)
    Client->>FullTime: 2. new FullTimeEmployee(张三, 20000)
    Note right of Client: 定义员工属性(姓名/薪资)

    %% 3. 将元素添加到对象结构
    Client->>EmpList: 3. addEmployee(FullTime)
    EmpList-->>Client: 3.1 元素添加成功
    Note left of EmpList: 内部维护员工列表

    %% 4. 初始化具体访问者(HR部门)
    Client->>HR: 4. new HRDepartment()
    Note right of Client: 创建执行“绩效评估”的访问者

    %% 5. 客户端触发对象结构的访问逻辑
    Client->>EmpList: 5. accept(HR)
    Note left of EmpList: 启动元素遍历与访问流程

    %% 6. 对象结构遍历元素,调用元素的accept方法(第一次分派)
    EmpList->>FullTime: 6. accept(HR)
    Note right of FullTime: 元素接收访问者,准备触发具体操作

    %% 7. 元素调用访问者的visit方法(第二次分派,双分派核心)
    FullTime->>HR: 7. visit(this)
    Note left of HR: 明确元素类型(FullTime),执行对应操作

    %% 8. 访问者执行具体业务逻辑(HR评估绩效)
    HR-->>FullTime: 8. 执行绩效评估(月薪≥15000→优秀)
    Note right of HR: 基于员工属性计算评估结果

    %% 9. 元素向对象结构返回操作结果
    FullTime-->>EmpList: 9. 评估结果返回
    Note left of EmpList: 接收当前元素的操作反馈

    %% 10. 对象结构向客户端返回最终结果
    EmpList-->>Client: 10. 所有员工评估完成
    Note right of Client: 整个访问流程结束

适用环境

访问者模式仅适用于以下场景,否则会放大其缺点:

  1. 数据结构稳定,操作多变:如集合、树形结构(如 XML/JSON 节点),且需频繁新增操作(如报表生成、数据校验、统计分析)。
  2. 需集中管理多组操作:如同一批数据需生成 “Excel 报表”“PDF 报表”“CSV 报表”,可对应三个访问者。
  3. 元素类型明确且固定:如元素仅为 “全职员工”“兼职员工”,不会频繁新增类型(否则需修改所有访问者)。
  4. 需遍历复杂对象结构:如遍历树形结构的每个节点,执行不同操作(如语法树分析、文件目录扫描)。

模式分析

访问者模式的核心是双分派机制和数据结构与操作的分离,需重点理解以下两点:

1. 双分派机制(Double Dispatch)

“分派” 指确定调用哪个方法的过程。访问者模式通过两次分派实现 “元素类型” 和 “访问者类型” 的动态绑定:

  • 第一次分派:客户端调用对象结构的accept(visitor),对象结构遍历元素,调用element.accept(visitor)(此时element是具体元素,visitor是具体访问者)。
  • 第二次分派:具体元素的accept方法中,调用visitor.visit(this)(this明确指向具体元素,触发访问者对该元素的特定操作)。

例如:fullTimeEmployee.accept(hrDepartment) → 内部调用hrDepartment.visit(fullTimeEmployee),最终执行 HR 部门对全职员工的绩效评估逻辑。

2. 分离的核心价值

  • 数据结构稳定:元素类(如FullTimeEmployee)只需定义accept方法,无需修改即可支持新操作(新增访问者即可)。
  • 操作独立扩展:新增操作(如 “行政部门登记员工信息”)只需新增AdminDepartment类(实现Visitor),无需修改元素类或对象结构。

模式扩展

访问者模式可结合其他模式进行扩展,满足更复杂需求:

1. 组合访问者(Composite Visitor)

结合组合模式,将多个访问者组合为一个 “复合访问者”,批量执行操作。例如:同时执行 HR 评估和财务计算,无需客户端分别调用。

// 复合访问者
public class CompositeDepartment implements Department {
    private List<Department> departments = new ArrayList<>();

    public void addDepartment(Department department) {
        departments.add(department);
    }

    @Override
    public void visit(FullTimeEmployee employee) {
        for (Department dept : departments) {
            dept.visit(employee);
        }
    }

    @Override
    public void visit(PartTimeEmployee employee) {
        for (Department dept : departments) {
            dept.visit(employee);
        }
    }
}

// 客户端使用
CompositeDepartment composite = new CompositeDepartment();
composite.addDepartment(new HRDepartment());
composite.addDepartment(new FinanceDepartment());
employeeList.accept(composite); // 一次调用,执行两个部门的操作

2. 带状态的访问者(Stateful Visitor)

访问者内部维护状态,遍历元素时累积状态(如统计总薪资、总人数)。例如:财务部门统计所有员工的薪资总和。

public class FinanceStatisticsDepartment implements Department {
    private int totalSalary = 0; // 状态:总薪资

    @Override
    public void visit(FullTimeEmployee employee) {
        totalSalary += employee.getSalary();
    }

    @Override
    public void visit(PartTimeEmployee employee) {
        totalSalary += employee.getHourlyWage() * 40; // 周薪折算为月薪(假设4周)
    }

    // 获取统计结果
    public int getTotalSalary() {
        return totalSalary;
    }
}

// 客户端使用
FinanceStatisticsDepartment stats = new FinanceStatisticsDepartment();
employeeList.accept(stats);
System.out.println("所有员工总薪资:" + stats.getTotalSalary() + "元");

3. 过滤访问者(Filtered Visitor)

结合过滤器模式,仅对符合条件的元素执行操作。例如:HR 部门仅评估月薪≥20000 的全职员工。

public class FilteredHRDepartment implements Department {
    @Override
    public void visit(FullTimeEmployee employee) {
        if (employee.getSalary() >= 20000) { // 过滤条件
            System.out.println("HR部门重点评估【高薪全职员工】" + employee.getName());
        }
    }

    @Override
    public void visit(PartTimeEmployee employee) {
        // 不处理兼职员工
    }
}

模式应用

访问者模式在框架和业务场景中应用广泛,典型案例包括:

  1. Java 语言:
    • javax.lang.model.element.ElementVisitor:注解处理器中,访问 Java 语法元素(类、方法、字段)的接口。
    • java.nio.file.FileVisitor:文件系统遍历中,访问文件 / 目录的接口(如Files.walkFileTree)。
  2. 编译器 / 解析器:
    • 语法分析阶段,访问抽象语法树(AST)的每个节点,执行语义检查、代码生成等操作(如 Java 编译器的com.sun.tools.javac.tree.TreeScanner)。
  3. 报表系统:
    • 同一批业务数据(如订单、用户)需生成 “销售额报表”“用户活跃度报表”“退款率报表”,对应三个访问者。
  4. 数据导出工具:
    • 数据库表数据需导出为 Excel、CSV、JSON 格式,每种格式对应一个访问者,遍历表结构并生成文件。

Android 中的应用

Android 框架和开发场景中,访问者模式主要用于 “结构稳定、操作多变” 的场景:

1. 注解处理器(APT)

Android 开发中,ButterKnife、Dagger2 等框架的注解处理器,核心依赖javax.lang.model.element.ElementVisitor(访问者模式):

  • 元素:Java 语法元素(TypeElement- 类、ExecutableElement- 方法、VariableElement- 字段)。
  • 访问者:框架自定义的访问者(如 ButterKnife 的BindViewProcessor),遍历元素并生成绑定代码。

2. 资源解析

Android 的AssetManager或Resources在解析资源(如 XML 布局、字符串)时,会遍历资源结构,使用访问者模式执行解析操作(如解析布局中的 View 节点)。

3. RecyclerView 遍历(自定义场景)

若需对 RecyclerView 的 Item 执行多种操作(如 “标记已读”“批量删除”“统计选中数”),可定义ItemVisitor接口,每个操作对应一个具体访问者,避免在 Adapter 中堆砌逻辑:

// 抽象访问者
public interface ItemVisitor {
    void visit(MessageItem item); // 访问“消息Item”
}

// 具体访问者:标记已读
public class MarkAsReadVisitor implements ItemVisitor {
    @Override
    public void visit(MessageItem item) {
        item.setRead(true);
    }
}

// Item元素
public class MessageItem {
    private boolean isRead;
    // ... 其他属性

    public void accept(ItemVisitor visitor) {
        visitor.visit(this);
    }

    public void setRead(boolean read) {
        isRead = read;
    }
}

4. 数据库操作(Room)

Room 框架在处理实体类(@Entity)时,会使用访问者模式解析实体的字段、主键、索引等信息,生成数据库操作代码(如Dao实现)。

代码实现(Java 版)

以 “公司部门访问员工” 为例,完整实现访问者模式:

1. 抽象访问者(Visitor)

定义访问所有具体元素的接口:

// 抽象访问者:部门
public interface Department {
    // 访问全职员工
    void visit(FullTimeEmployee employee);
    // 访问兼职员工
    void visit(PartTimeEmployee employee);
}

2. 具体访问者(ConcreteVisitor)

实现具体操作(HR 评估绩效、财务计算薪资):

// 具体访问者1:HR部门(评估绩效)
public class HRDepartment implements Department {
    @Override
    public void visit(FullTimeEmployee employee) {
        System.out.println("HR部门评估【全职员工】" + employee.getName() + ":绩效优秀(月薪≥15000)");
    }

    @Override
    public void visit(PartTimeEmployee employee) {
        System.out.println("HR部门评估【兼职员工】" + employee.getName() + ":绩效合格(时薪≥50)");
    }
}

// 具体访问者2:财务部门(计算薪资)
public class FinanceDepartment implements Department {
    @Override
    public void visit(FullTimeEmployee employee) {
        int salary = employee.getSalary();
        System.out.println("财务部门计算【全职员工】" + employee.getName() + ":月薪=" + salary + "元");
    }

    @Override
    public void visit(PartTimeEmployee employee) {
        int weeklyWage = employee.getHourlyWage() * 40; // 假设每周工作40小时
        System.out.println("财务部门计算【兼职员工】" + employee.getName() + ":周薪=" + weeklyWage + "元");
    }
}

3. 抽象元素(Element)

定义接受访问者的接口:

// 抽象元素:员工
public interface Employee {
    // 接受访问者访问
    void accept(Department department);
    // 获取员工姓名(供访问者使用)
    String getName();
}

4. 具体元素(ConcreteElement)

实现accept方法,触发访问者操作:

// 具体元素1:全职员工
public class FullTimeEmployee implements Employee {
    private String name;
    private int salary; // 月薪

    public FullTimeEmployee(String name, int salary) {
        this.name = name;
        this.salary = salary;
    }

    @Override
    public void accept(Department department) {
        // 调用访问者的visit方法(双分派的关键)
        department.visit(this);
    }

    // Getter(供访问者获取内部状态)
    @Override
    public String getName() {
        return name;
    }

    public int getSalary() {
        return salary;
    }
}

// 具体元素2:兼职员工
public class PartTimeEmployee implements Employee {
    private String name;
    private int hourlyWage; // 时薪

    public PartTimeEmployee(String name, int hourlyWage) {
        this.name = name;
        this.hourlyWage = hourlyWage;
    }

    @Override
    public void accept(Department department) {
        department.visit(this);
    }

    // Getter
    @Override
    public String getName() {
        return name;
    }

    public int getHourlyWage() {
        return hourlyWage;
    }
}

5. 对象结构(ObjectStructure)

管理元素集合,提供访问入口:

// 对象结构:员工列表
import java.util.ArrayList;
import java.util.List;

public class EmployeeList {
    // 存储所有员工
    private List<Employee> employees = new ArrayList<>();

    // 添加员工
    public void addEmployee(Employee employee) {
        employees.add(employee);
    }

    // 接受访问者访问(遍历所有员工)
    public void accept(Department department) {
        for (Employee employee : employees) {
            employee.accept(department);
        }
    }
}

6. 客户端(Client)

测试代码,模拟部门访问员工:

public class Client {
    public static void main(String[] args) {
        // 1. 创建对象结构和元素
        EmployeeList employeeList = new EmployeeList();
        employeeList.addEmployee(new FullTimeEmployee("张三", 20000));
        employeeList.addEmployee(new PartTimeEmployee("李四", 60));
        employeeList.addEmployee(new FullTimeEmployee("王五", 15000));

        // 2. HR部门访问员工(评估绩效)
        System.out.println("=== HR部门访问员工 ===");
        Department hr = new HRDepartment();
        employeeList.accept(hr);

        // 3. 财务部门访问员工(计算薪资)
        System.out.println("\n=== 财务部门访问员工 ===");
        Department finance = new FinanceDepartment();
        employeeList.accept(finance);
    }
}

7. 运行结果

=== HR部门访问员工 ===
HR部门评估【全职员工】张三:绩效优秀(月薪≥15000)
HR部门评估【兼职员工】李四:绩效合格(时薪≥50)
HR部门评估【全职员工】王五:绩效优秀(月薪≥15000)

=== 财务部门访问员工 ===
财务部门计算【全职员工】张三:月薪=20000元
财务部门计算【兼职员工】李四:周薪=2400元
财务部门计算【全职员工】王五:月薪=15000元

实现2

public interface Element {
   void accept(Visitor visitor);
}

class CustomerGroup {

  private List<Customer> customers = new ArrayList<>();

   void accept(Visitor visitor) {
       for (Customer customer : customers) {
           customer.accept(visitor);
       }
   }

   void addCustomer(Customer customer) {
       customers.add(customer);
   }

}

public class Customer implements Element {

   private String name;

   private List<Order> orders = new ArrayList<>();


   Customer(String name) {
        this.name = name;
    }



​    String getName() {

​        return name;

​    }



​    void addOrder(Order order) {

​        orders.add(order);

​    }



​    public void accept(Visitor visitor) {

​        visitor.visit(this);

​        for (Order order : orders) {

​            order.accept(visitor);

​        }

​    }

}

public class Order implements Element {



​    private String name;

​    private List<Item> items = new ArrayList();



​    Order(String name) {

​        this.name = name;

​    }



​    Order(String name, String itemName) {

​        this.name = name;

​        this.addItem(new Item(itemName));

​    }



​    String getName() {

​        return name;

​    }



​    void addItem(Item item) {

​        items.add(item);

​    }



​    public void accept(Visitor visitor) {

​        visitor.visit(this);



​        for (Item item : items) {

​            item.accept(visitor);

​        }

​    }

}

public class Item implements Element {



​    private String name;



​    Item(String name) {

​        this.name = name;

​    }



​    String getName() {

​        return name;

​    }



​    public void accept(Visitor visitor) {

​        visitor.visit(this);

​    }

}

public interface Visitor {

​    void visit(Customer customer);



​    void visit(Order order);



​    void visit(Item item);

}

public class GeneralReport implements Visitor {



​    private int customersNo;

​    private int ordersNo;

​    private int itemsNo;



​    public void visit(Customer customer) {

​        System.out.println(customer.getName());

​        customersNo++;

​    }



​    public void visit(Order order) {

​        System.out.println(order.getName());

​        ordersNo++;

​    }



​    public void visit(Item item) {

​        System.out.println(item.getName());

​        itemsNo++;

​    }



​    public void displayResults() {

​        System.out.println("Number of customers: " + customersNo);

​        System.out.println("Number of orders:    " + ordersNo);

​        System.out.println("Number of items:     " + itemsNo);

​    }

}

public class Client {

​    public static void main(String[] args) {

​        Customer customer1 = new Customer("customer1");

​        customer1.addOrder(new Order("order1", "item1"));

​        customer1.addOrder(new Order("order2", "item1"));

​        customer1.addOrder(new Order("order3", "item1"));



​        Order order = new Order("order_a");

​        order.addItem(new Item("item_a1"));

​        order.addItem(new Item("item_a2"));

​        order.addItem(new Item("item_a3"));

​        Customer customer2 = new Customer("customer2");

​        customer2.addOrder(order);



​        CustomerGroup customers = new CustomerGroup();

​        customers.addCustomer(customer1);

​        customers.addCustomer(customer2);



​        GeneralReport visitor = new GeneralReport();

​        customers.accept(visitor);

​        visitor.displayResults();

​    }

}

customer1

order1

item1

order2

item1

order3

item1

customer2

order_a

item_a1

item_a2

item_a3

Number of customers: 2

Number of orders:    4

Number of items:     6

总结

访问者模式是一把 “双刃剑”,其核心价值在于分离数据结构与操作,但依赖于 “数据结构稳定” 的前提:

  1. 核心优势:
    • 操作可独立扩展,符合开闭原则;
    • 集中管理同类操作,简化元素类;
    • 支持复杂对象结构的遍历与多维度处理。
  2. 核心局限:
    • 破坏元素封装性,依赖Getter暴露状态;
    • 元素类型变更困难,违反开闭原则;
    • 依赖关系复杂,增加代码理解成本。
  3. 使用建议:
    • 仅当数据结构稳定、操作多变时使用(如编译器、报表系统);
    • 避免在元素类型频繁变更的场景中使用(如业务迭代快的核心模块);
    • 可结合组合模式、过滤器模式扩展功能,平衡灵活性与可维护性。

访问者模式不是 “银弹”,需根据业务场景权衡其优缺点,避免为了 “用模式而用模式”。

最近更新:: 2025/10/22 15:36
Contributors: 罗凯文, luokaiwen