Java设计模式再相识 (二十三)——访问者模式

目录

  • 访问者模式(Visitor Pattern)
  • 什么是访问者模式?
  • 模式结构
  • 示例:员工访问器
    • 员工抽象类
    • 程序员类
    • 产品经理类
    • 访问者接口
    • 具体访问者:打印信息
    • 具体访问者:发年终奖
    • 员工容器类
    • 使用示例
      • 输出结果
  • 小结
  • 适用场景

访问者模式(Visitor Pattern)

在我们开发中,有时候会遇到这样一种情况:一个对象结构中有很多不同类型的对象,而我们需要对这些对象执行一些“额外操作”,但又不希望修改这些对象本身的代码。比如:

  • 对一组不同类型的文件做压缩、备份等处理
  • 对公司组织架构中的员工做统计、加薪等操作
  • 编译器中,对语法树节点进行类型检查或代码生成

这时候就可以考虑使用 访问者模式(Visitor Pattern) 来解决。

什么是访问者模式?

访问者模式是一种行为型设计模式,它将数据结构与作用于结构上的操作分离,通过引入访问者对象,将操作从对象内部抽离出去,从而在不修改类的前提下扩展新的行为。

一句话总结就是:让你能在不动原有类结构的基础上,新增对这些类的“外部操作”。

模式结构

访问者模式主要由以下几个角色组成:

  • Visitor(访问者):定义对元素的访问操作,每个元素类型一个方法。
  • ConcreteVisitor(具体访问者):实现访问操作,定义具体行为。
  • Element(元素接口):定义一个 accept(Visitor) 方法,用于接收访问者。
  • ConcreteElement(具体元素):实现 accept() 方法,通常会将自己传回访问者。
  • ObjectStructure(对象结构):存储多个元素,提供访问者的入口。

示例:员工访问器

我们以“公司员工”这个例子来讲。公司中有两类员工:程序员和产品经理,我们希望对他们分别进行:

  • 打印信息(比如姓名、岗位、薪资)
  • 发放年终奖金

我们不希望把打印和奖金发放的逻辑塞进员工类里,否则违反单一职责,也不利于扩展。

员工抽象类

public interface Employee {
    void accept(Visitor visitor); // 接收访问者
}

程序员类

public class Engineer implements Employee {
    private String name;
    private int codeLinesPerDay;

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

    public String getName() {
        return name;
    }

    public int getCodeLinesPerDay() {
        return codeLinesPerDay;
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this); // 回调访问者
    }
}

产品经理类

public class ProductManager implements Employee {
    private String name;
    private int productCount;

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

    public String getName() {
        return name;
    }

    public int getProductCount() {
        return productCount;
    }

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

访问者接口

public interface Visitor {
    void visit(Engineer engineer);
    void visit(ProductManager pm);
}

具体访问者:打印信息

public class InfoPrinter implements Visitor {
    @Override
    public void visit(Engineer engineer) {
        System.out.println("程序员:" + engineer.getName() + ",每天写 " + engineer.getCodeLinesPerDay() + " 行代码");
    }

    @Override
    public void visit(ProductManager pm) {
        System.out.println("产品经理:" + pm.getName() + ",管理 " + pm.getProductCount() + " 个产品");
    }
}

具体访问者:发年终奖

public class BonusVisitor implements Visitor {
    @Override
    public void visit(Engineer engineer) {
        int bonus = engineer.getCodeLinesPerDay() * 10;
        System.out.println("程序员:" + engineer.getName() + " 年终奖:" + bonus);
    }

    @Override
    public void visit(ProductManager pm) {
        int bonus = pm.getProductCount() * 1000;
        System.out.println("产品经理:" + pm.getName() + " 年终奖:" + bonus);
    }
}

员工容器类

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

public class Company {
    private List<Employee> employees = new ArrayList<>();

    public void add(Employee e) {
        employees.add(e);
    }

    public void show(Visitor visitor) {
        for (Employee e : employees) {
            e.accept(visitor);
        }
    }
}

使用示例

public class Main {
    public static void main(String[] args) {
        Company company = new Company();
        company.add(new Engineer("小张", 500));
        company.add(new ProductManager("小李", 3));

        System.out.println("打印员工信息:");
        company.show(new InfoPrinter());

        System.out.println("发放年终奖:");
        company.show(new BonusVisitor());
    }
}

输出结果

打印员工信息:
程序员:小张,每天写 500 行代码
产品经理:小李,管理 3 个产品
发放年终奖:
程序员:小张 年终奖:5000
产品经理:小李 年终奖:3000

小结

访问者模式的核心就是将操作抽离出去,让你在不修改元素类的前提下增加新的行为,非常适合对一组对象结构进行“多种操作”的场景。比如打印、导出、统计、计算、格式转换等都可以作为访问者来扩展。

不过它也有一个缺点,就是每新增一个元素类(比如新增一类员工),你就需要修改所有访问者类,违反了开闭原则中的“对修改关闭”。所以它适用于稳定的数据结构、但多变的行为的场景。

适用场景

  • 对对象结构中的元素进行多种操作,如导出、分析、监控
  • 编译器中的语法树遍历和处理
  • 数据分析系统中的指标收集器
  • 元素结构固定,但操作逻辑不断增加的系统
Java设计模式行为型模式
2025 © Yeliheng的技术小站 版权所有