23种设计模式23种设计模式
首页
介绍
  • 单例模式
  • 工厂方法模式
  • 抽象工厂模式
  • 建造者模式
  • 原型模式
  • 适配器模式
  • 桥接模式
  • 组合模式
  • 装饰器模式
  • 外观模式
  • 享元模式
  • 代理模式
  • 责任链模式
  • 命令模式
  • 解释器模式
  • 迭代器模式
  • 中介者模式
  • 备忘录模式
  • 观察者模式
  • 状态模式
  • 策略模式
  • 模板方法模式
  • 访问者模式
🚀 编程指南
首页
介绍
  • 单例模式
  • 工厂方法模式
  • 抽象工厂模式
  • 建造者模式
  • 原型模式
  • 适配器模式
  • 桥接模式
  • 组合模式
  • 装饰器模式
  • 外观模式
  • 享元模式
  • 代理模式
  • 责任链模式
  • 命令模式
  • 解释器模式
  • 迭代器模式
  • 中介者模式
  • 备忘录模式
  • 观察者模式
  • 状态模式
  • 策略模式
  • 模板方法模式
  • 访问者模式
🚀 编程指南
  • 行为型模式

    • 责任链模式 (Chain of Responsibility)
    • 命令模式 (Command)
    • 解释器模式 (Interpreter)
    • 迭代器模式 (Iterator)
    • 中介者模式 (Mediator)
    • 备忘录模式 (Memento)
    • 观察者模式 (Observer)
    • 状态模式 (State)
    • 策略模式 (Strategy)
    • 模板方法模式 (Template Method)
    • 访问者模式 (Visitor)

访问者模式 (Visitor)

📖 通俗理解

年终考核:

  • HR 来了:评估员工的考勤、态度
  • 财务来了:核算员工的工资、奖金
  • CTO 来了:评估员工的技术能力

不同的"访问者"对员工进行不同维度的评估,而不需要修改员工类。

访问者模式就是:在不改变元素类的前提下,定义新的操作。

🎯 解决什么问题?

当需要对一组对象执行不同的操作,且不想修改这些对象的类时。

🌰 生活中的例子

  • 年终考核:不同部门的人考核不同维度
  • 税务检查:对不同资产有不同的检查方式
  • 编译器:对 AST 节点进行不同操作(类型检查、代码生成)

💻 Java 代码实现

场景:员工绩效考核

/**
 * 访问者接口
 */
public interface Visitor {
    void visit(Engineer engineer);
    void visit(Manager manager);
}

/**
 * 员工接口
 */
public interface Employee {
    void accept(Visitor visitor);
}

/**
 * 工程师
 */
public class Engineer implements Employee {
    private String name;
    private int codeLines;
    
    public Engineer(String name, int codeLines) {
        this.name = name;
        this.codeLines = codeLines;
    }
    
    public String getName() { return name; }
    public int getCodeLines() { return codeLines; }
    
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

/**
 * 经理
 */
public class Manager implements Employee {
    private String name;
    private int projectCount;
    
    public Manager(String name, int projectCount) {
        this.name = name;
        this.projectCount = projectCount;
    }
    
    public String getName() { return name; }
    public int getProjectCount() { return projectCount; }
    
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

/**
 * KPI 考核访问者
 */
public class KPIVisitor implements Visitor {
    @Override
    public void visit(Engineer engineer) {
        System.out.println("工程师 " + engineer.getName() + 
            " 代码量: " + engineer.getCodeLines() + " 行");
    }
    
    @Override
    public void visit(Manager manager) {
        System.out.println("经理 " + manager.getName() + 
            " 管理项目数: " + manager.getProjectCount() + " 个");
    }
}

/**
 * 财务审计访问者
 */
public class FinanceVisitor implements Visitor {
    @Override
    public void visit(Engineer engineer) {
        double salary = engineer.getCodeLines() * 0.1;
        System.out.println("工程师 " + engineer.getName() + 
            " 绩效工资: ¥" + salary);
    }
    
    @Override
    public void visit(Manager manager) {
        double bonus = manager.getProjectCount() * 1000;
        System.out.println("经理 " + manager.getName() + 
            " 项目奖金: ¥" + bonus);
    }
}

使用:

public class Client {
    public static void main(String[] args) {
        List<Employee> employees = Arrays.asList(
            new Engineer("张三", 5000),
            new Engineer("李四", 8000),
            new Manager("王总", 3)
        );
        
        System.out.println("=== KPI 考核 ===");
        Visitor kpiVisitor = new KPIVisitor();
        for (Employee e : employees) {
            e.accept(kpiVisitor);
        }
        
        System.out.println("\n=== 财务审计 ===");
        Visitor financeVisitor = new FinanceVisitor();
        for (Employee e : employees) {
            e.accept(financeVisitor);
        }
    }
}

输出:

=== KPI 考核 ===
工程师 张三 代码量: 5000 行
工程师 李四 代码量: 8000 行
经理 王总 管理项目数: 3 个

=== 财务审计 ===
工程师 张三 绩效工资: ¥500.0
工程师 李四 绩效工资: ¥800.0
经理 王总 项目奖金: ¥3000.0

⚠️ 双重分派

访问者模式使用了"双重分派"技术:

  1. 第一次分派:调用 element.accept(visitor),根据 element 类型选择方法
  2. 第二次分派:调用 visitor.visit(this),根据 visitor 类型选择方法

✅ 适用场景

  1. 对象结构稳定,但经常需要新增操作
  2. 需要对一组对象执行不同的操作
  3. 数据结构与操作分离

⚠️ 优缺点

优点:

  • 新增操作容易(新增 Visitor)
  • 相关操作集中在一个 Visitor 中

缺点:

  • 新增元素困难(需要修改所有 Visitor)
  • 破坏封装(Visitor 需要访问元素内部状态)

小结

访问者模式:在不修改元素类的情况下,定义新的操作。

适合:操作经常变化,但数据结构稳定的场景。

应用:编译器 AST 处理、文档转换、复杂对象结构的批量操作


🎉 恭喜你完成了 23 种设计模式的学习!

👉 返回首页

Prev
模板方法模式 (Template Method)