设计模式——访问者模式
一、访问者模式核心概念访问者模式Visitor Pattern是一种行为型设计模式它的核心思想是将数据结构与对数据的操作分离。简单来说就是当你有一组固定的对象结构但需要频繁新增不同的操作逻辑时不需要修改这些对象本身而是把操作逻辑封装成 “访问者”让对象接受访问者的 “访问” 并执行对应的操作。可以用一个比喻理解博物馆里的展品固定的数据结构不会变但不同的参观者访问者会对展品做不同的操作游客只是看、研究员记录信息、保洁员清洁展品只需要提供一个 “接受访问” 的接口具体操作由参观者决定。核心角色抽象元素Element定义一个接受访问者的接口通常是accept(Visitor)方法。具体元素ConcreteElement实现抽象元素的接口调用访问者的对应方法。抽象访问者Visitor为每个具体元素定义一个访问方法visit(ConcreteElement)。具体访问者ConcreteVisitor实现抽象访问者的方法定义对具体元素的具体操作。对象结构ObjectStructure管理元素的集合提供遍历元素的接口供访问者遍历访问。二、C 代码示例我们以 “动物园” 为场景动物园里有老虎、猴子两种固定的动物数据结构需要新增 “喂食”“体检” 两种操作访问者用访问者模式实现这个需求。#include iostream #include vector #include memory // 前置声明抽象元素需要知道抽象访问者反之亦然 class Visitor; // ---------------------- 1. 抽象元素Element ---------------------- class Animal { public: virtual ~Animal() default; // 核心接口接受访问者的访问 virtual void accept(Visitor visitor) 0; }; // ---------------------- 2. 具体元素ConcreteElement ---------------------- // 老虎 class Tiger : public Animal { public: // 接受访问者调用访问者针对老虎的方法 void accept(Visitor visitor) override { visitor.visit(*this); // 把自身传递给访问者 } // 老虎的特有属性/方法 void roar() const { std::cout 老虎嗷呜 std::endl; } std::string getName() const { return 老虎; } }; // 猴子 class Monkey : public Animal { public: void accept(Visitor visitor) override { visitor.visit(*this); } // 猴子的特有属性/方法 void jump() const { std::cout 猴子蹦蹦跳跳 std::endl; } std::string getName() const { return 猴子; } }; // ---------------------- 3. 抽象访问者Visitor ---------------------- class Visitor { public: virtual ~Visitor() default; // 为每个具体元素定义访问方法 virtual void visit(Tiger tiger) 0; virtual void visit(Monkey monkey) 0; }; // ---------------------- 4. 具体访问者ConcreteVisitor ---------------------- // 喂食访问者 class FeedVisitor : public Visitor { public: void visit(Tiger tiger) override { std::cout 给 tiger.getName() 喂肉 std::endl; tiger.roar(); // 可以调用元素的特有方法 } void visit(Monkey monkey) override { std::cout 给 monkey.getName() 喂香蕉 std::endl; monkey.jump(); } }; // 体检访问者 class CheckVisitor : public Visitor { public: void visit(Tiger tiger) override { std::cout 给 tiger.getName() 检查牙齿和爪子 std::endl; } void visit(Monkey monkey) override { std::cout 给 monkey.getName() 检查尾巴和视力 std::endl; } }; // ---------------------- 5. 对象结构ObjectStructure ---------------------- class Zoo { private: std::vectorstd::unique_ptrAnimal animals; // 管理元素集合 public: // 添加动物 void addAnimal(std::unique_ptrAnimal animal) { animals.push_back(std::move(animal)); } // 让访问者遍历所有动物并执行操作 void accept(Visitor visitor) { for (auto animal : animals) { animal-accept(visitor); } } }; // ---------------------- 测试代码 ---------------------- int main() { // 1. 创建动物园并添加动物 Zoo zoo; zoo.addAnimal(std::make_uniqueTiger()); zoo.addAnimal(std::make_uniqueMonkey()); // 2. 喂食操作 std::cout 执行喂食操作 std::endl; FeedVisitor feedVisitor; zoo.accept(feedVisitor); // 3. 体检操作 std::cout \n 执行体检操作 std::endl; CheckVisitor checkVisitor; zoo.accept(checkVisitor); return 0; }代码运行结果 执行喂食操作 给老虎喂肉 老虎嗷呜 给猴子喂香蕉 猴子蹦蹦跳跳 执行体检操作 给老虎检查牙齿和爪子 给猴子检查尾巴和视力代码关键解释核心接口accept动物类Element的accept方法接收一个访问者对象并调用访问者的visit方法同时把自身作为参数传递。这一步是 “双重分派”先根据动物类型Tiger/Monkey调用对应的accept再根据访问者类型Feed/Check调用对应的visit最终确定执行哪个操作。新增操作的扩展如果需要新增 “打扫笼子” 操作只需要新增一个CleanVisitor类实现Visitor接口无需修改任何动物类和现有访问者类符合 “开闭原则”。对象结构Zoo封装了元素的集合管理和遍历逻辑让客户端只需调用zoo.accept(visitor)就能批量执行操作简化了客户端代码。#include iostream #include vector #include memory #include cmath // 前置声明抽象访问者 class ShapeVisitor; // ---------------------- 1. 抽象元素Shape ---------------------- class Shape { public: virtual ~Shape() default; // 核心接受访问者的接口 virtual void accept(ShapeVisitor visitor) 0; }; // ---------------------- 2. 具体元素Circle, Rectangle ---------------------- class Circle : public Shape { private: double radius_; public: explicit Circle(double radius) : radius_(radius) {} void accept(ShapeVisitor visitor) override; double getRadius() const { return radius_; } }; class Rectangle : public Shape { private: double width_; double height_; public: Rectangle(double width, double height) : width_(width), height_(height) {} void accept(ShapeVisitor visitor) override; double getWidth() const { return width_; } double getHeight() const { return height_; } }; // ---------------------- 3. 抽象访问者ShapeVisitor ---------------------- class ShapeVisitor { public: virtual ~ShapeVisitor() default; // 为每种具体图形声明访问方法 virtual void visit(Circle circle) 0; virtual void visit(Rectangle rectangle) 0; }; // ---------------------- 4. 具体访问者AreaCalculator ---------------------- class AreaCalculator : public ShapeVisitor { private: double totalArea_ 0.0; // 访问者可累积状态如总面积 public: void visit(Circle circle) override { double area M_PI * circle.getRadius() * circle.getRadius(); std::cout 圆形面积: area std::endl; totalArea_ area; } void visit(Rectangle rectangle) override { double area rectangle.getWidth() * rectangle.getHeight(); std::cout 矩形面积: area std::endl; totalArea_ area; } double getTotalArea() const { return totalArea_; } }; // 具体元素的accept方法实现需在ShapeVisitor声明后定义 void Circle::accept(ShapeVisitor visitor) { visitor.visit(*this); // 将自身Circle类型传递给访问者 } void Rectangle::accept(ShapeVisitor visitor) { visitor.visit(*this); // 将自身Rectangle类型传递给访问者 } // ---------------------- 5. 对象结构ShapeCollection ---------------------- class ShapeCollection { private: std::vectorstd::unique_ptrShape shapes_; public: void addShape(std::unique_ptrShape shape) { shapes_.push_back(std::move(shape)); } // 允许访问者遍历所有图形 void accept(ShapeVisitor visitor) { for (auto shape : shapes_) { shape-accept(visitor); // 触发双重分派 } } }; // ---------------------- 客户端测试代码 ---------------------- int main() { // 1. 创建图形集合 ShapeCollection collection; collection.addShape(std::make_uniqueCircle(5.0)); // 圆形半径5 collection.addShape(std::make_uniqueRectangle(4.0, 6.0)); // 矩形宽4高6 collection.addShape(std::make_uniqueCircle(2.0)); // 圆形半径2 // 2. 创建面积计算访问者 AreaCalculator areaCalc; // 3. 执行面积计算操作 std::cout 计算各图形面积 std::endl; collection.accept(areaCalc); // 4. 输出累计总面积访问者内部状态的体现 std::cout \n所有图形总面积: areaCalc.getTotalArea() std::endl; return 0; }计算各图形面积 圆形面积: 78.5398 矩形面积: 24 圆形面积: 12.5664 所有图形总面积: 115.1062三、访问者模式的适用场景数据结构稳定如示例中的动物类型固定但需要频繁新增不同的操作逻辑。需要对一组对象进行多种不相关的操作且不想让这些操作污染对象本身。需要集中管理一组对象的某类操作如所有动物的体检逻辑都在CheckVisitor中便于维护。四、访问者模式的优缺点表格优点缺点操作与数据结构分离新增操作只需加访问者符合开闭原则新增元素类型时需要修改所有访问者的接口违反开闭原则集中管理同类操作代码可读性、维护性更高访问者可能需要访问元素的私有属性破坏封装性可通过友元 / 提供接口解决可以在访问者中积累状态如统计所有动物的喂食总量双重分派逻辑增加了代码复杂度新手不易理解总结访问者模式的核心是分离数据结构和操作通过 “接受 - 访问” 的双重分派机制实现对不同元素的差异化操作。C 实现的关键是抽象元素定义accept接口抽象访问者为每个具体元素定义visit接口具体元素调用访问者的对应visit方法。适用场景是 “数据结构固定、操作频繁变化”缺点是新增元素类型时成本高需权衡使用。

相关新闻

Copyparty系统配置与部署:从零到生产环境的完整指南

Copyparty系统配置与部署:从零到生产环境的完整指南

Copyparty系统配置与部署:从零到生产环境的完整指南 【免费下载链接】copyparty Portable file server with accelerated resumable uploads, dedup, WebDAV, SFTP, FTP, TFTP, zeroconf, media indexer, thumbnails all in one file 项目地址: https://gitcode.c…

2026/6/23 6:42:34阅读更多 →
PIMI:基于惯性动量的并行概率伊辛机硬件加速架构详解

PIMI:基于惯性动量的并行概率伊辛机硬件加速架构详解

1. 项目概述:当伊辛机遇上硬件加速最近几年,计算领域里一个词挺火,叫“伊辛机”。听起来有点玄乎,但它本质上是一种受物理现象启发的计算模型,特别擅长解决组合优化这类让传统计算机头疼的问题。简单来说,它…

2026/6/23 6:42:34阅读更多 →
空天立体全天候透视监测·动态目标全息重构·网状自愈专网实战练兵一体化平台

空天立体全天候透视监测·动态目标全息重构·网状自愈专网实战练兵一体化平台

空天立体全天候透视监测动态目标全息重构网状自愈专网实战练兵一体化平台一、平台总体定位本平台为空天通感算训一体化实战中枢,依托3000米高空长效驻浮空天载体构建立体感知体系,集全天候云雾暗夜透视监测、全域目标动态全息三维重构、网状抗毁自愈宽带…

2026/6/23 6:42:34阅读更多 →
强力NCM音频解锁方案:如何一键将加密音乐转换为MP3/FLAC格式

强力NCM音频解锁方案:如何一键将加密音乐转换为MP3/FLAC格式

强力NCM音频解锁方案:如何一键将加密音乐转换为MP3/FLAC格式 【免费下载链接】NCMconverter NCMconverter将ncm文件转换为mp3或者flac文件 项目地址: https://gitcode.com/gh_mirrors/nc/NCMconverter NCMconverter是一款专业的音频格式转换工具,…

2026/6/23 7:57:39阅读更多 →
ARM Cortex-M4低功耗设计实战:Kinetis K10模式解析与优化指南

ARM Cortex-M4低功耗设计实战:Kinetis K10模式解析与优化指南

1. 项目概述:为什么Kinetis K10的低功耗设计值得深究在嵌入式开发领域,尤其是面对电池供电的物联网节点、便携式医疗设备或长期部署的传感器时,功耗管理从来都不是一个“锦上添花”的选项,而是决定产品成败的核心指标。很多开发者…

2026/6/23 7:57:39阅读更多 →
B站会员购抢票神器biliTickerBuy:快速上手与高效配置完全指南

B站会员购抢票神器biliTickerBuy:快速上手与高效配置完全指南

B站会员购抢票神器biliTickerBuy:快速上手与高效配置完全指南 【免费下载链接】biliTickerBuy b站会员购购票辅助工具 项目地址: https://gitcode.com/GitHub_Trending/bi/biliTickerBuy 还在为B站会员购热门活动的门票秒光而烦恼吗?biliTickerBu…

2026/6/23 7:57:39阅读更多 →
ivi未来路线图:Web UI库的发展趋势和技术演进

ivi未来路线图:Web UI库的发展趋势和技术演进

ivi未来路线图:Web UI库的发展趋势和技术演进 【免费下载链接】ivi Lighweight Embeddable Web UI Library 项目地址: https://gitcode.com/gh_mirrors/iv/ivi ivi作为一款轻量级可嵌入Web UI库,正引领着前端开发的新潮流。本文将深入探讨ivi的技…

2026/6/23 7:57:39阅读更多 →
三次搬家,三次选择:StorHub趣存自助仓如何赢得用户长期信赖

三次搬家,三次选择:StorHub趣存自助仓如何赢得用户长期信赖

对于在上海打拼的人来说,“搬家”几乎是必修课。每一次为了更便利的通勤、更好的居住环境而迁移,都伴随着一次对个人物品的“断舍离”考验。心爱的书籍、换季的衣物、充满回忆的老物件,常常因为新家空间的局限而陷入无处安放的窘境。我们需要…

2026/6/23 7:57:39阅读更多 →
告别繁琐配置!用rime-auto-deploy一键部署20款Rime输入法皮肤

告别繁琐配置!用rime-auto-deploy一键部署20款Rime输入法皮肤

告别繁琐配置!用rime-auto-deploy一键部署20款Rime输入法皮肤 【免费下载链接】rime-auto-deploy Rime输入法安装脚本,让一切更轻松。Make using Rime easy. 项目地址: https://gitcode.com/gh_mirrors/ri/rime-auto-deploy 你是否曾经为Rime输入…

2026/6/23 7:52:39阅读更多 →
【人工智能】一文搞定到底什么是智能体

【人工智能】一文搞定到底什么是智能体

【人工智能】一文搞定到底什么是智能体 一文搞定到底什么是智能体【人工智能】一文搞定到底什么是智能体一. LM,WorkFlow,Agent分别有什么么不同二. Agent的思考过程是怎样的三. Agent的五个核心部分1)LLM2)Prompt3)Me…

2026/6/23 7:04:52阅读更多 →
嵌入式GUI控件实战:ROTARY、SCROLLBAR、SLIDER原理与应用

嵌入式GUI控件实战:ROTARY、SCROLLBAR、SLIDER原理与应用

1. 嵌入式GUI控件:从原理到实战的深度解析在嵌入式系统开发中,图形用户界面(GUI)的设计与实现往往是项目从“能用”到“好用”的关键一跃。不同于资源充沛的PC或移动平台,嵌入式设备的GUI需要在有限的CPU性能、内存空间…

2026/6/23 1:55:32阅读更多 →
Google AI Studio 300美元额度的真相与实战指南

Google AI Studio 300美元额度的真相与实战指南

1. 这300美金不是“送钱”,而是Google埋下的第一道技术门槛 你看到标题里那个醒目的“$300美金”时,第一反应可能是:又一个免费额度?领完就完事?我亲手试过——这300美金根本不是红包,而是一张入场券&…

2026/6/23 5:55:37阅读更多 →
2026年京东云 618 活动 Hermes Agent/OpenClaw配置Token Plan新手必看指南

2026年京东云 618 活动 Hermes Agent/OpenClaw配置Token Plan新手必看指南

2026年京东云 618 活动 Hermes Agent/OpenClaw配置Token Plan新手必看指南。OpenClaw是开源的个人AI助手,Hermes Agent则是一个能自我进化的AI智能体框架。阿里云提供计算巢、轻量服务器及无影云电脑三种部署OpenClaw 与 Hermes Agent的方案、百炼Token Plan兼容主流…

2026/6/23 0:00:38阅读更多 →
2026年北京电子沙盘制作公司深度评测:从技术选型到落地效果,谁在真正定义“数字+实体”的融合边界?

2026年北京电子沙盘制作公司深度评测:从技术选型到落地效果,谁在真正定义“数字+实体”的融合边界?

模块一:行业背景——百亿赛道爆发,北京市场的特殊性与选型困局2026年,电子沙盘行业已走过“要不要做”的讨论,进入“找谁做、怎么做”的深水区。据行业研究机构数据,2025年国内电子沙盘市场规模已突破85亿元&#xff0…

2026/6/23 0:00:38阅读更多 →
音视频场景下的 Java 开发者面试:技术与挑战

音视频场景下的 Java 开发者面试:技术与挑战

面试互联网大厂:从音视频场景看 Java 开发者的技能与挑战 在互联网大厂求职的面试中,Java 开发者往往需要面对严苛的技术问题。今天,我们将通过一位名叫燕双非的搞笑程序员与严肃的面试官之间的对话,看看在音视频场景下&#xff0…

2026/6/23 0:00:38阅读更多 →