Skip to content

访问者模式

访问者模式(Visitor Pattern)是一种行为设计模式,它允许你在不修改已有类的情况下向这些类添加新的功能。通过将算法与对象结构分离,访问者模式使得我们可以对一组对象进行操作而不改变它们的类定义。其核心思想是双重分派:首先确定具体类型,然后根据这个类型执行相应的操作。

访问者模式的主要角色

  1. Visitor(访问者接口):声明了针对不同元素类型的访问操作。
  2. ConcreteVisitor(具体访问者):实现了每个由 Visitor 声明的操作,实现针对具体元素的操作逻辑。
  3. Element(元素接口):声明了一个accept方法,接收一个访问者对象作为参数。
  4. ConcreteElement(具体元素):实现了 Element 接口,并在accept方法中调用访问者的访问方法。
  5. ObjectStructure(对象结构):可以是一个组合模式的实例,或者是一个简单的集合如列表或数组,它持有一组元素,并提供一个方法来遍历这些元素。

在 JavaScript 中的实现

下面是一个简单的例子来说明如何在 JavaScript 中使用访问者模式:

示例:员工薪资计算系统

假设我们有一个公司管理系统,其中包含不同类型员工(如工程师和经理),我们需要根据不同类型的员工计算他们的年终奖金。

javascript
// Visitor(访问者接口)
class Visitor {
  visitEngineer(engineer) {
    throw new Error("This method should be overridden");
  }

  visitManager(manager) {
    throw new Error("This method should be overridden");
  }
}

// ConcreteVisitor(具体访问者)
class BonusCalculator extends Visitor {
  visitEngineer(engineer) {
    console.log(
      `Calculating bonus for Engineer ${engineer.name}: Base bonus + 10%`
    );
    return engineer.salary * 0.1;
  }

  visitManager(manager) {
    console.log(
      `Calculating bonus for Manager ${manager.name}: Base bonus + 15%`
    );
    return manager.salary * 0.15;
  }
}

// Element(元素接口)
class Employee {
  constructor(name, salary) {
    this.name = name;
    this.salary = salary;
  }

  accept(visitor) {
    throw new Error("This method should be overridden");
  }
}

// ConcreteElement(具体元素)
class Engineer extends Employee {
  accept(visitor) {
    return visitor.visitEngineer(this);
  }
}

class Manager extends Employee {
  accept(visitor) {
    return visitor.visitManager(this);
  }
}

// ObjectStructure(对象结构)
class Company {
  constructor() {
    this.employees = [];
  }

  addEmployee(employee) {
    this.employees.push(employee);
  }

  calculateBonuses(visitor) {
    let totalBonus = 0;
    for (const employee of this.employees) {
      totalBonus += employee.accept(visitor);
    }
    return totalBonus;
  }
}

// 使用示例
const company = new Company();
company.addEmployee(new Engineer("John", 100000));
company.addEmployee(new Manager("Jane", 120000));

const bonusCalculator = new BonusCalculator();
console.log(
  `Total bonuses: $${company.calculateBonuses(bonusCalculator).toFixed(2)}`
);
// 输出:
// Calculating bonus for Engineer John: Base bonus + 10%
// Calculating bonus for Manager Jane: Base bonus + 15%
// Total bonuses: $28000.00

代码解释

  • Visitor(访问者接口):定义了针对不同类型员工的访问方法 visitEngineervisitManager
  • BonusCalculator(具体访问者):实现了Visitor接口的方法,提供了具体的奖金计算逻辑。
  • Employee(元素接口):所有员工的基类,包含了基本信息如姓名和薪资,并声明了accept方法。
  • Engineer 和 Manager(具体元素):分别实现了accept方法,接受访问者并调用相应的方法。
  • Company(对象结构):维护了一个员工列表,并提供了一个方法来计算所有员工的奖金总和。

应用场景

访问者模式适用于以下几种情况:

  • 当你需要对一组对象进行许多不同的并且互不相关的操作时,而不想修改这些对象的类定义。
  • 当需要对数据结构中的元素执行某些依赖于其类型的操作时。
  • 当操作需要访问对象内部状态,并且这种访问方式不能直接通过公共接口提供时。

通过使用访问者模式,可以在不影响已有类的情况下为这些类添加新功能,增强了系统的灵活性和可扩展性。然而,随着元素种类的增加,访问者类也需要不断扩展,这可能会导致一定的复杂性。

Released under the MIT License.