当前位置: 首页 > news >正文

访问者模式C++

访问者模式(Visitor Pattern)是一种行为型设计模式,它允许你在不修改集合元素类的前提下,为集合中的元素添加新的操作。这种模式将操作与元素的结构分离,使操作可以独立变化。

访问者模式的核心角色

  1. Visitor(访问者接口):声明对每个元素类的访问操作
  2. ConcreteVisitor(具体访问者):实现访问者接口,定义对每个元素的具体操作
  3. Element(元素接口):声明接受访问者的接口(accept()方法)
  4. ConcreteElement(具体元素):实现元素接口,通过accept()方法接收访问者
  5. ObjectStructure(对象结构):包含元素集合,提供遍历元素的方法

C++实现示例

以下以"企业员工数据统计"为例实现访问者模式,统计不同类型员工(工程师、经理)的工作时长、项目数量等信息:

#include <iostream>
#include <string>
#include <vector>
#include <memory>// 前向声明
class Engineer;
class Manager;// 访问者接口
class Visitor {
public:virtual ~Visitor() = default;virtual void visit(Engineer* engineer) = 0;    // 访问工程师virtual void visit(Manager* manager) = 0;      // 访问经理
};// 元素接口:员工
class Employee {
protected:std::string name;    // 姓名int id;              // 工号public:Employee(std::string n, int i) : name(std::move(n)), id(i) {}virtual ~Employee() = default;const std::string& getName() const { return name; }int getId() const { return id; }// 接受访问者virtual void accept(Visitor* visitor) = 0;
};// 具体元素1:工程师
class Engineer : public Employee {
private:int workHours;       // 工作时长(小时)int projectCount;    // 参与项目数public:Engineer(std::string n, int i, int hours, int projects): Employee(std::move(n), i), workHours(hours), projectCount(projects) {}int getWorkHours() const { return workHours; }int getProjectCount() const { return projectCount; }// 接受访问者,调用访问者的对应方法void accept(Visitor* visitor) override {visitor->visit(this);}
};// 具体元素2:经理
class Manager : public Employee {
private:int teamSize;        // 团队规模int managedProjects; // 管理项目数double budget;       // 管理预算(万元)public:Manager(std::string n, int i, int size, int projects, double budg): Employee(std::move(n), i), teamSize(size), managedProjects(projects), budget(budg) {}int getTeamSize() const { return teamSize; }int getManagedProjects() const { return managedProjects; }double getBudget() const { return budget; }// 接受访问者,调用访问者的对应方法void accept(Visitor* visitor) override {visitor->visit(this);}
};// 具体访问者1:工作统计访问者
class WorkStatsVisitor : public Visitor {
private:int totalWorkHours = 0;    // 总工作时长int totalProjects = 0;     // 总项目数int engineerCount = 0;     // 工程师数量int managerCount = 0;      // 经理数量public:void visit(Engineer* engineer) override {engineerCount++;totalWorkHours += engineer->getWorkHours();totalProjects += engineer->getProjectCount();std::cout << "统计工程师: " << engineer->getName() << ", 工作时长: " << engineer->getWorkHours() << ", 参与项目: " << engineer->getProjectCount() << std::endl;}void visit(Manager* manager) override {managerCount++;totalProjects += manager->getManagedProjects();std::cout << "统计经理: " << manager->getName() << ", 管理项目: " << manager->getManagedProjects() << ", 团队规模: " << manager->getTeamSize() << std::endl;}// 显示统计结果void showStats() const {std::cout << "\n===== 工作统计结果 =====" << std::endl;std::cout << "工程师总数: " << engineerCount << std::endl;std::cout << "经理总数: " << managerCount << std::endl;std::cout << "总工作时长: " << totalWorkHours << "小时" << std::endl;std::cout << "总项目数: " << totalProjects << std::endl;}
};// 具体访问者2:成本统计访问者
class CostVisitor : public Visitor {
private:double totalSalary = 0.0;  // 总薪资double totalBudget = 0.0;  // 总预算public:void visit(Engineer* engineer) override {// 假设工程师时薪100元double salary = engineer->getWorkHours() * 100;totalSalary += salary;std::cout << "计算工程师 " << engineer->getName() << " 薪资: " << salary << "元" << std::endl;}void visit(Manager* manager) override {// 假设经理月薪固定20000元 + 预算10%double salary = 20000 + manager->getBudget() * 10000 * 0.1;totalSalary += salary;totalBudget += manager->getBudget();std::cout << "计算经理 " << manager->getName() << " 薪资: " << salary << "元, 管理预算: " << manager->getBudget() << "万元" << std::endl;}// 显示成本统计结果void showCosts() const {std::cout << "\n===== 成本统计结果 =====" << std::endl;std::cout << "总薪资支出: " << totalSalary << "元" << std::endl;std::cout << "总管理预算: " << totalBudget << "万元" << std::endl;}
};// 对象结构:员工集合
class EmployeeList {
private:std::vector<std::unique_ptr<Employee>> employees;public:// 添加员工void addEmployee(std::unique_ptr<Employee> employee) {employees.push_back(std::move(employee));}// 接受访问者,让所有员工被访问void accept(Visitor* visitor) {for (const auto& emp : employees) {emp->accept(visitor);}}
};// 客户端代码
int main() {// 创建员工集合EmployeeList company;// 添加员工company.addEmployee(std::make_unique<Engineer>("张三", 1001, 160, 3));company.addEmployee(std::make_unique<Engineer>("李四", 1002, 180, 2));company.addEmployee(std::make_unique<Manager>("王五", 2001, 8, 5, 500));company.addEmployee(std::make_unique<Engineer>("赵六", 1003, 150, 4));company.addEmployee(std::make_unique<Manager>("钱七", 2002, 12, 3, 800));// 工作统计WorkStatsVisitor workStats;std::cout << "=== 工作统计 ===" << std::endl;company.accept(&workStats);workStats.showStats();// 成本统计CostVisitor costStats;std::cout << "\n=== 成本统计 ===" << std::endl;company.accept(&costStats);costStats.showCosts();return 0;
}

代码解析

  1. 访问者接口(Visitor

    • 声明了对每种具体元素(EngineerManager)的访问方法
    • 新增加的操作只需实现这个接口,无需修改元素类
  2. 元素接口(Employee

    • 声明了接受访问者的accept()方法
    • 所有具体元素都必须实现这个方法,将自身传递给访问者
  3. 具体元素

    • EngineerManager分别实现了accept()方法,调用访问者对应的visit()方法
    • 元素类只包含自身的数据和基本行为,不包含统计等业务操作
  4. 具体访问者

    • WorkStatsVisitor实现了工作时长和项目数量的统计
    • CostVisitor实现了薪资和预算的统计
    • 每个访问者专注于一类操作,便于扩展新的统计维度
  5. 对象结构(EmployeeList

    • 维护员工集合,提供accept()方法让所有员工被访问者访问
    • 简化了客户端代码,无需逐个遍历元素

访问者模式的优缺点

优点

  • 分离了数据结构与操作,新增操作只需添加新的访问者,无需修改元素类
  • 集中管理相关操作,同一类操作被封装在一个访问者中
  • 便于为不同类型的元素提供不同的操作
  • 可以在不修改元素的情况下,对元素进行复杂的统计或转换

缺点

  • 增加新元素类型时,需要修改所有访问者接口和实现,违反开放-封闭原则
  • 元素类必须暴露内部状态给访问者,可能破坏封装性
  • 访问者与元素之间存在紧密耦合,增加了系统复杂度

适用场景

  • 当需要对一个包含多种类型对象的集合进行多种不相关的操作时
  • 当需要为现有元素添加新操作,且不希望修改元素类时
  • 当元素类相对稳定,而操作经常变化或增加时

常见应用:

  • 数据报表生成(对同一组数据生成不同报表)
  • 编译器的语法树分析(不同阶段对语法树进行不同处理)
  • 复杂集合的批量操作(如过滤、转换、统计等)
  • 文档解析器(对不同类型的文档元素进行不同处理)

访问者模式的关键是双分派技术:元素的accept()方法调用访问者的visit()方法,而visit()方法又使用元素的具体类型,从而实现对不同元素执行不同操作。

http://www.xdnf.cn/news/1305991.html

相关文章:

  • Android RxJava 过滤与条件操作详解
  • 数据结构初阶(17)排序算法——非比较排序、排序算法总结
  • Flink的状态管理
  • SpringCloud学习
  • 【完整源码+数据集+部署教程】孔洞检测系统源码和数据集:改进yolo11-RetBlock
  • 自适应UI设计解读 | Fathom 企业人工智能平台
  • ​​金仓数据库KingbaseES V9R1C10安装教程 - Windows版详细指南​
  • 力扣习题:基本计算器
  • 从 “碳足迹“ 到 “零碳圈“:上海零碳园区的改造密码
  • Xget:为您的开发工作流解锁极致速度
  • 用 1 张 4090 复现 GPT-3.5?——单卡 24 GB 的「渐进式重计算」训练实践
  • 第三十五天(JSAjax技术)
  • 苹果(apple)ios系统和安卓(Android) apk系统开发者账号类型及申请步骤
  • Linux之高可用集群实战(二)
  • 利用Minicsv库解析csv文件的c程序及读入测试
  • expand.exe命令为什么能显示CD.iso的版本信息?
  • 中国象棋人机对战
  • Linux Namespace隔离实战:dd/mkfs/mount/unshare构建终极沙箱
  • Spring Boot 静态函数无法自动注入 Bean?深入解析与解决方案
  • MySQL 主键详解:作用与使用方法
  • 嵌入式:Linux软件编程:线程
  • 【详细操作指南】如何将 Moodle 与编辑器连接,以修改文档、检查和批改作业等
  • 2025年最新油管视频下载,附MassTube下载软件地址
  • 【Canvas与玻璃光】铝圈蓝底玻璃光按钮
  • 华为实验综合小练习
  • YAML:锚点深度解析,告别重复,拥抱优雅的配置艺术
  • 第二十四天:虚函数与纯虚函数
  • 【科研绘图系列】R语言绘制三维曲线图
  • MySQL多表查询案例
  • 关系型数据库从入门到精通:MySQL 核心知识全解析