在软件设计中,我们常常会遇到需要对一组对象进行不同操作的情况。比如,在一个公司里,你可能需要对不同的员工(如经理、程序员、设计师等)进行评估,每种员工的评估方式可能不同。如果每次评估都需要去修改员工类的代码,这就非常麻烦,尤其是当员工种类很多时,修改起来会非常复杂。

访问者模式(Visitor)为我们提供了一种方法,它允许我们在不改变对象类的前提下,对对象集合中的不同元素执行不同的操作。通过将操作封装到访问者对象中,我们可以动态地为对象添加新的功能,而不需要修改这些对象的代码。

趣味解读:公司员工评估

假设你是一个公司的人力资源经理,你的任务是对不同岗位的员工进行评估。每个岗位的评估方式都不相同:

  • 经理需要评估领导能力和决策能力。

  • 程序员需要评估编码能力和技术深度。

  • 设计师需要评估创意和设计感。

你需要对不同的员工类型(经理、程序员、设计师)执行不同的评估任务,但是你又不想频繁修改员工的类。这时,访问者模式可以帮助你实现这一目标。你可以定义一个“评估者”类来为每种员工提供不同的评估策略,而不需要改变员工类的代码。

Java代码案例:员工评估系统

假设你正在开发一个员工评估系统,其中有多个类型的员工。你可以使用访问者模式为每种员工提供不同的评估方法。

// 1. 员工接口
interface Employee {
    void accept(Evaluator evaluator);
}
​
// 2. 具体员工类 - 经理
class Manager implements Employee {
    @Override
    public void accept(Evaluator evaluator) {
        evaluator.evaluate(this);  // 经理接受评估
    }
}
​
// 3. 具体员工类 - 程序员
class Programmer implements Employee {
    @Override
    public void accept(Evaluator evaluator) {
        evaluator.evaluate(this);  // 程序员接受评估
    }
}
​
// 4. 具体员工类 - 设计师
class Designer implements Employee {
    @Override
    public void accept(Evaluator evaluator) {
        evaluator.evaluate(this);  // 设计师接受评估
    }
}
​
// 5. 访问者接口
interface Evaluator {
    void evaluate(Manager manager);
    void evaluate(Programmer programmer);
    void evaluate(Designer designer);
}
​
// 6. 具体评估者 - 绩效评估
class PerformanceEvaluator implements Evaluator {
    @Override
    public void evaluate(Manager manager) {
        System.out.println("评估经理的领导力和决策能力...");
    }
​
    @Override
    public void evaluate(Programmer programmer) {
        System.out.println("评估程序员的编码能力和技术深度...");
    }
​
    @Override
    public void evaluate(Designer designer) {
        System.out.println("评估设计师的创意和设计感...");
    }
}
​
// 7. 具体评估者 - 薪资评估
class SalaryEvaluator implements Evaluator {
    @Override
    public void evaluate(Manager manager) {
        System.out.println("评估经理的薪资结构...");
    }
​
    @Override
    public void evaluate(Programmer programmer) {
        System.out.println("评估程序员的薪资水平...");
    }
​
    @Override
    public void evaluate(Designer designer) {
        System.out.println("评估设计师的薪资待遇...");
    }
}
​
// 8. 客户端代码
public class Main {
    public static void main(String[] args) {
        // 创建不同的员工对象
        Employee manager = new Manager();
        Employee programmer = new Programmer();
        Employee designer = new Designer();
​
        // 创建评估者对象
        Evaluator performanceEvaluator = new PerformanceEvaluator();
        Evaluator salaryEvaluator = new SalaryEvaluator();
​
        // 每个员工接受不同的评估
        System.out.println("绩效评估:");
        manager.accept(performanceEvaluator);
        programmer.accept(performanceEvaluator);
        designer.accept(performanceEvaluator);
​
        System.out.println("\n薪资评估:");
        manager.accept(salaryEvaluator);
        programmer.accept(salaryEvaluator);
        designer.accept(salaryEvaluator);
    }
}

代码解析:

  1. 员工接口 (Employee):这是所有员工类的父类或接口,定义了 accept(Evaluator evaluator) 方法,用于接收评估者的操作。

  2. 具体员工类ManagerProgrammerDesigner 是具体的员工类型,每个员工都实现了 accept() 方法,接受评估者的评估。

  3. 评估者接口 (Evaluator):这是所有评估类的父类或接口,定义了对不同员工类型的评估方法。每个评估者类可以对不同的员工类型执行不同的评估操作。

  4. 具体评估者类PerformanceEvaluatorSalaryEvaluator 是具体的评估者类,分别负责绩效评估和薪资评估。它们为不同员工提供不同的评估逻辑。

  5. 客户端代码:在 Main 类中,创建了不同类型的员工和评估者对象。通过 accept() 方法,员工接受评估,评估者则对不同的员工执行不同的评估操作。

运行结果:

绩效评估:
评估经理的领导力和决策能力...
评估程序员的编码能力和技术深度...
评估设计师的创意和设计感...
​
薪资评估:
评估经理的薪资结构...
评估程序员的薪资水平...
评估设计师的薪资待遇...

实际应用场景

  1. 图形绘制系统:在图形绘制系统中,可能有不同的形状(如圆形、矩形、三角形等)。你可以使用访问者模式来实现不同的绘制操作,而不需要修改每个形状类的代码。

  2. 编译器设计:在编译器中,访问者模式可以用于语法树的遍历和处理。每个节点代表一个不同的语法元素,访问者模式可以帮助你在不修改语法元素类的情况下执行不同的操作,如类型检查、代码生成等。

  3. 文件系统操作:在文件系统中,可能需要对文件和目录执行不同的操作(如计算大小、备份等)。访问者模式可以用于封装这些操作,并对文件和目录进行不同的处理。

总结

访问者模式允许我们在不改变对象类的前提下,对一组对象执行不同的操作。通过将操作封装到访问者对象中,我们能够动态地为这些对象添加新的功能。访问者模式特别适用于那些对象结构较为稳定,但需要频繁为这些对象添加新操作的场景。它能够避免修改对象类的代码,提高代码的灵活性和扩展性。