JavaScript设计模式「基于ES2024」:行为型模式-访问者模式

51 阅读2分钟

访问者模式是一种行为设计模式,它允许你在不改变各元素的类的前提下定义作用于这些元素的新操作。这种模式适用于数据结构相对稳定,但经常需要在其上定义新的操作的场景。

// 元素接口
class Employee {
    accept(visitor) {
        throw new Error("Method 'accept' must be implemented.");
    }
}

// 具体元素类
class Developer extends Employee {
    #name;
    #salary;
    #hours;

    constructor(name, salary, hours) {
        super();
        this.#name = name;
        this.#salary = salary;
        this.#hours = hours;
    }

    accept(visitor) {
        return visitor.visitDeveloper(this);
    }

    get name() { return this.#name; }
    get salary() { return this.#salary; }
    get hours() { return this.#hours; }
}

class Designer extends Employee {
    #name;
    #salary;
    #projects;

    constructor(name, salary, projects) {
        super();
        this.#name = name;
        this.#salary = salary;
        this.#projects = projects;
    }

    accept(visitor) {
        return visitor.visitDesigner(this);
    }

    get name() { return this.#name; }
    get salary() { return this.#salary; }
    get projects() { return this.#projects; }
}

// 访问者接口
class Visitor {
    visitDeveloper(developer) {
        throw new Error("Method 'visitDeveloper' must be implemented.");
    }

    visitDesigner(designer) {
        throw new Error("Method 'visitDesigner' must be implemented.");
    }
}

// 具体访问者类
class SalaryCalculator extends Visitor {
    visitDeveloper(developer) {
        const overtimePay = Math.max(0, developer.hours - 40) * (developer.salary / 40) * 1.5;
        return developer.salary + overtimePay;
    }

    visitDesigner(designer) {
        const bonus = designer.projects * 1000;
        return designer.salary + bonus;
    }
}

class AnnualReportGenerator extends Visitor {
    visitDeveloper(developer) {
        return `Developer: ${developer.name}, Annual Salary: $${developer.salary * 52}, Hours Worked: ${developer.hours * 52}`;
    }

    visitDesigner(designer) {
        return `Designer: ${designer.name}, Annual Salary: $${designer.salary * 52}, Projects Completed: ${designer.projects}`;
    }
}

// 对象结构类
class Company {
    #employees = [];

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

    accept(visitor) {
        return this.#employees.map(employee => employee.accept(visitor));
    }
}

// 客户端代码
function demonstrateVisitor() {
    const company = new Company();
    company.addEmployee(new Developer("John Doe", 1000, 45));
    company.addEmployee(new Designer("Jane Smith", 1200, 3));
    company.addEmployee(new Developer("Bob Johnson", 1100, 40));

    console.log("Calculating Salaries:");
    const salaryCalculator = new SalaryCalculator();
    const salaries = company.accept(salaryCalculator);
    salaries.forEach((salary, index) => {
        console.log(`Employee ${index + 1}: $${salary.toFixed(2)}`);
    });

    console.log("\nGenerating Annual Report:");
    const reportGenerator = new AnnualReportGenerator();
    const reports = company.accept(reportGenerator);
    reports.forEach(report => console.log(report));
}

demonstrateVisitor();

实现思路

  1. Employee 接口(元素接口)

    • 定义了 accept 方法,这是访问者模式的核心。
  2. 具体元素类(Developer, Designer

    • 实现了 Employee 接口。
    • 包含特定于员工类型的数据。
    • accept 方法调用访问者的相应方法。
  3. Visitor 接口

    • 为每种具体元素类型定义了访问方法。
  4. 具体访问者类(SalaryCalculator, AnnualReportGenerator

    • 实现了 Visitor 接口。
    • 为每种元素类型提供了特定的操作实现。
  5. Company 类(对象结构)

    • 管理一组元素(员工)。
    • 提供了一个 accept 方法来允许访问者访问其所有元素。

优点

  • 开闭原则:可以引入新的访问者来定义新的操作,而不需要修改现有元素类。
  • 单一职责原则:将数据结构和数据操作分离。
  • 灵活性:可以轻松添加新的操作,而不需要修改现有类。
  • 积累操作:访问者可以在遍历过程中累积状态。