C++实现设计模式---访问者模式 (Visitor)
访问者模式 (Visitor)
访问者模式 是一种行为型设计模式,它允许你在不修改现有类的情况下向这些类添加新的行为。访问者模式将操作逻辑与对象结构分离,通过访问者对象实现新的行为。
意图
- 通过将操作与数据结构分离,使得在不改变数据结构的前提下定义新的操作。
- 使用访问者模式可以避免在对象中添加过多的方法。
使用场景
- 对象结构稳定,但需要增加新的操作:
- 数据结构相对稳定,但需要在上面定义多个操作。
- 需要跨多个类定义操作:
- 操作涉及多个类,但不希望将操作逻辑耦合到这些类中。
- 不希望污染类的接口:
- 通过访问者模式,避免在类中添加与操作无关的方法。
参与者角色
- 访问者接口 (Visitor)
- 定义所有访问方法的接口,每种数据结构都需要对应的访问方法。
- 具体访问者 (ConcreteVisitor)
- 实现访问者接口,定义具体的操作。
- 元素接口 (Element)
- 定义接受访问者的方法,通常为
accept(Visitor&)
。
- 定义接受访问者的方法,通常为
- 具体元素 (ConcreteElement)
- 实现元素接口,调用访问者中的方法。
- 对象结构 (ObjectStructure)
- 管理元素对象的集合,可以让访问者遍历这些元素。
示例代码
以下代码展示了访问者模式的实现,用于模拟不同类型员工的薪资计算(如全职员工和兼职员工)。
#include <iostream>
#include <vector>
#include <memory>
// 前置声明访问者接口
class FullTimeEmployee;
class PartTimeEmployee;
// 访问者接口
class Visitor {
public:
virtual ~Visitor() = default;
// 为不同元素定义访问方法
virtual void visit(FullTimeEmployee& employee) = 0;
virtual void visit(PartTimeEmployee& employee) = 0;
};
// 元素接口
class Employee {
public:
virtual ~Employee() = default;
// 接受访问者
virtual void accept(Visitor& visitor) = 0;
};
// 具体元素:全职员工
class FullTimeEmployee : public Employee {
private:
std::string name;
double salary;
public:
FullTimeEmployee(std::string name, double salary)
: name(std::move(name)), salary(salary) {}
const std::string& getName() const { return name; }
double getSalary() const { return salary; }
void accept(Visitor& visitor) override {
visitor.visit(*this); // 调用访问者的访问方法
}
};
// 具体元素:兼职员工
class PartTimeEmployee : public Employee {
private:
std::string name;
double hourlyRate;
public:
PartTimeEmployee(std::string name, double hourlyRate)
: name(std::move(name)), hourlyRate(hourlyRate) {}
const std::string& getName() const { return name; }
double getHourlyRate() const { return hourlyRate; }
void accept(Visitor& visitor) override {
visitor.visit(*this); // 调用访问者的访问方法
}
};
// 具体访问者:薪资计算
class SalaryCalculator : public Visitor {
public:
void visit(FullTimeEmployee& employee) override {
std::cout << "计算全职员工 " << employee.getName()
<< " 的月薪:" << employee.getSalary() << " 元。
";
}
void visit(PartTimeEmployee& employee) override {
std::cout << "计算兼职员工 " << employee.getName()
<< " 的时薪:" << employee.getHourlyRate() << " 元。
";
}
};
// 对象结构:员工集合
class EmployeeList {
private:
std::vector<std::shared_ptr<Employee>> employees;
public:
void addEmployee(std::shared_ptr<Employee> employee) {
employees.push_back(std::move(employee));
}
void accept(Visitor& visitor) {
for (const auto& employee : employees) {
employee->accept(visitor); // 让访问者操作每个员工
}
}
};
// 客户端代码
int main() {
EmployeeList employeeList;
// 添加员工
employeeList.addEmployee(std::make_shared<FullTimeEmployee>("Alice", 8000));
employeeList.addEmployee(std::make_shared<PartTimeEmployee>("Bob", 50));
// 创建访问者
SalaryCalculator calculator;
// 计算薪资
employeeList.accept(calculator);
return 0;
}
代码解析
1. 访问者接口 (Visitor)
- 定义了针对不同元素类型的访问方法。
- 每种元素都需要对应一个访问方法。
class Visitor {
public:
virtual ~Visitor() = default;
virtual void visit(FullTimeEmployee& employee) = 0;
virtual void visit(PartTimeEmployee& employee) = 0;
};
2. 元素接口 (Employee)
- 定义了
accept
方法,接受访问者对象。 accept
方法会将自身传递给访问者。
class Employee {
public:
virtual ~Employee() = default;
virtual void accept(Visitor& visitor) = 0;
};
3. 具体元素类
FullTimeEmployee
和PartTimeEmployee
实现了元素接口。- 它们通过调用访问者的对应方法,实现了与访问者的交互。
class FullTimeEmployee : public Employee {
void accept(Visitor& visitor) override {
visitor.visit(*this);
}
};
4. 具体访问者 (SalaryCalculator)
- 实现了访问者接口。
- 包含了对全职员工和兼职员工的具体操作逻辑。
class SalaryCalculator : public Visitor {
void visit(FullTimeEmployee& employee) override {
std::cout << "计算全职员工的薪资。
";
}
void visit(PartTimeEmployee& employee) override {
std::cout << "计算兼职员工的薪资。
";
}
};
5. 对象结构 (EmployeeList)
- 用于管理多个元素,并提供
accept
方法将访问者应用到每个元素。
class EmployeeList {
void accept(Visitor& visitor) {
for (const auto& employee : employees) {
employee->accept(visitor);
}
}
};
优缺点
优点
- 扩展性强:
- 可以在不修改元素类的情况下添加新的操作。
- 符合开闭原则:
- 通过增加新的访问者实现功能扩展。
- 集中管理逻辑:
- 访问者模式将相关操作集中在访问者类中,方便管理。
缺点
- 违反依赖倒置原则:
- 访问者模式依赖具体类,而非接口。
- 对象结构不稳定时扩展困难:
- 如果元素类经常变化,访问者需要频繁修改。
- 复杂性增加:
- 元素类需要对访问者暴露内部细节,可能增加复杂性。
适用场景
- 需要对对象结构中的对象执行多种操作:
- 如薪资计算、数据统计等。
- 对象结构稳定,但操作经常变化:
- 访问者模式可以方便地扩展新操作。
- 希望避免在类中添加不相关方法:
- 通过访问者模式,将操作逻辑从类中分离。
总结
访问者模式通过将操作逻辑封装到访问者中,实现了对象结构与操作逻辑的分离。它特别适用于对象结构稳定,而操作经常变化的场景。