S19. 访问者模式

2023-10-24 15:30
文章标签 模式 访问者 s19

本文主要是介绍S19. 访问者模式,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

访问者模式

将作用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元素提供多种访问方式。

简单的说,就是已经存在稳定的数据类。由于外部需求,需要访问特定的类成员。希望在不改变原数据类接口,仅通过增加外部模块实现需求。此模式,是行为模式中最复杂的一种模式。

意义

此模式主要用于在存在多个同类型的数据类情况下,统一对这些数据类某个成员属性的访问方式。有助于将数据代码与业务代码解耦,可在不修改数据类的情况下自由增加访问方式业务。

访问者模式-类图.png

应用场景

上述分析了,此模式多用于存在多个同类型数据类,只访问这些类某个成员属性。例:电脑管家检测电脑,要一项一项检测,先功能检测、再驱动检测。其中功能检测,只检测各配件(GPU、声卡)的功能是否正常。驱动检测,只检测各配件驱动是否正常。(具体如何检测的,这里不做关注)

分析

由上述电脑管家检测场景,可构建类图:
![电脑管家场景]

访问者场景类图.png

数据源类: GPU(CPartGpu)、声卡(CPartSoundCard),两者可抽象出基类电脑组件(CComputePartBase);访问类: 功能访问(CVisitorFunction)、驱动访问(CVisitorDriver),两者可抽象访问基类(CVisitorBase);管理类: 电脑管家(CSafeMgr)。

注: 在最初的访问者模式类图没有管理类的角色,这里为了方便客户端使用接口,才增加此类。实际场景中,只要运用到访问者模式思想即可,没有必要参照其实现方式生搬硬套。

源码实现

编程环境

  1. 编译环境: Linux环境
  2. 语言: C++语言
  3. 编译命令: make

工程结构

Visitor/
├── compute_part_base.h
├── main.cc
├── Makefile
├── part_gpu.cc
├── part_gpu.h
├── part_sound_card.cc
├── part_sound_card.h
├── safe_manager.cc
├── safe_manager.h
├── visitor_base.h
├── visitor_driver.cc
├── visitor_driver.h
├── visitor_function.cc
└── visitor_function.h
  • compute_part、part_gpu、part_sound_card: 源数据类封装。
  • vistor_base、vistor_driver、visitor_function: 对源数据类的访问方式类。
  • safe_manager: 管理类
  • main.cc: 客户端代码,程序入口
  • Makefile: 编译工具

源数据接口

  1. 电脑配件基类
class CComputePartBase
{
public:CComputePartBase() {}virtual ~CComputePartBase() {} virtual std::string GetName() = 0;virtual void Accept(CVisitorBase *visitor) = 0;
};
  • 配件基类声明纯虚函数Accept(), 依赖CVisitorBase,主要为外部访问类提供入口。通过此接口,各个访问类就可以触发自己内部的检测方式。
  1. GPU配件
class CPartGpu : public CComputePartBase
{
public:explicit CPartGpu(std::string name, int driver, int function);~CPartGpu();std::string GetName();int CheckDriver();int CheckFunction();void Accept(CVisitorBase *visitor);private:std::string mName;int mDriver;int mFunction;
};

其内部实现了Accept()接口,看下Accept()做了什么事情。

void CPartGpu::Accept(CVisitorBase *visitor)
{if (NULL != visitor) {visitor->VisitGpu(this);} else {GPU_LOGE("visitor is NULL!\n");}
}
  • 可看到Accept内部调用了CVisitorBase,并将自身数据传参至其成员函数VisitGpu()。如此一来,CVisitorBase就能够访问CPartGpu对象的成员了。
  • 现已将CPartGpu对象指针传递给CVisitorBase,至于其访问哪些数据,则由CVisitorBase派生的各个子类来具体实现。即不同业务的子类根据自身需求,访问CPartGpu不同的成员属性。
  • CPartSoundCard逻辑与CPartGpu大体相似,这里不做分析。

访问者接口

  1. 访问者基类
class CVisitorBase
{
public:virtual std::string GetName() = 0;virtual int VisitGpu(CPartGpu *gpu) = 0;virtual int VisitSoundCard(CPartSoundCard *soundCard) = 0;
};
  • 访问者基类统一访问配件的接口,在CComputePartBase的Accept()接口的实现中会被使用到。
  • 一种类型的配件会对应一个Visit接口。在硬件已知的情况下,这些接口基本会固定无需修改。大多数情况下,不会在硬件固定的情况下,还会增加意料之外的配件。
  1. 驱动访问者
class CVisitorDriver : public CVisitorBase
{
public:CVisitorDriver();~CVisitorDriver();std::string GetName();int VisitGpu(CPartGpu *gpu);int VisitSoundCard(CPartSoundCard *soundCard);private: std::string mName;
};

驱动访问者是具体的访问者类,其会实现VisitGpu、VisitSoundCard但是其只关心Gpu和SoundCard的驱动属性。而功能访问者只关心Gpu和SoundCard的功能属性。

查看下VisitGpu()、VisitSoundCard()接口的实现:

int CVisitorDriver::VisitGpu(CPartGpu *gpu)
{if (gpu->CheckDriver() <= 0) {DRV_LOG("%s of Gpu Failed!\n", this->GetName().c_str());} else {DRV_LOG("%s of Gpu Success!\n", this->GetName().c_str());}        return 0;
}int CVisitorDriver::VisitSoundCard(CPartSoundCard *soundCard)
{if (NULL == soundCard) {DRV_LOGE("soundCard is NULL!\n");}if (soundCard->CheckDriver() <= 0) {DRV_LOG("%s of SoundCard Failed!\n", this->GetName().c_str());} else {DRV_LOG("%s of SoundCard Success!\n", this->GetName().c_str());}return 0;
}
  • 这两个接口都是通过具体的配件对象来访问其内部的成员属性。即访问者类通过配件类提供的接口访问配件内部信息,通过这些属性可做一些业务逻辑。实现数据与业务解耦,数据放在配件类,业务放在访问者内或者更外层。

管理类

typedef enum
{ITEM_DRIVER = 0,ITEM_FUNCTION,ITEM_ALL,
}ECheckItem;class CSafeMgr
{
public:CSafeMgr();~CSafeMgr();void AddPartArray(CComputePartBase *);void Accept(CVisitorBase *visitor);void CheckItem(ECheckItem item);private:CPartGpu         *mpGpu;CPartSoundCard   *mpSoundCard;CVisitorDriver   theDriverCheck;CVisitorFunction theFunctionCheck;std::vector<CComputePartBase *> mPartArray; 
};

电脑管家就是管理类的作用,主要用于整理所有源数据对象与访问者对象。实现各个场景接口,为客户端提供简单易用的接口。

具体实现:

void CSafeMgr::AddPartArray(CComputePartBase *pPart)
{if (NULL == pPart) {MGR_LOGE("pPart is NULL!\n");return ;}mPartArray.push_back(pPart);
}void CSafeMgr::Accept(CVisitorBase *visitor)
{vector<CComputePartBase *>::iterator it;for (it = mPartArray.begin(); it != mPartArray.end(); ++it) {(*it)->Accept(visitor);}
}void CSafeMgr::CheckItem(ECheckItem item)
{switch (item){case ITEM_DRIVER:Accept(&theDriverCheck);break;case ITEM_FUNCTION:Accept(&theFunctionCheck);break;case ITEM_ALL:Accept(&theDriverCheck);Accept(&theFunctionCheck);break;default:MGR_LOGW("No this item!\n");break;} 
}
  • CSafeMgr将配件对象注册到mPartArray,当需要检测指定项时,便依次变量所用配件的Accept(Item)接口,完成所有配件该项检测。
  • CSafeMgr只是为了方便客户端使用配件类和访问者类。实际访问者模式的设计中没有此角色,可参考意义上的类图。

客户端代码

int main(int argc, char *argv[])
{CSafeMgr theSafeMgr;theSafeMgr.CheckItem(ITEM_ALL);return 0;
}

因为设计了CSafeMgr接口,main代码就简单易懂,只是运用CSafeMgr检测指定项即可。其无需关注有多少配件和多少测试项,只需关注结果。

总结

  • 满足单一职责原则。访问者模式将数据与业务解耦,将同一行为的不同内容移植特定的类。
  • 满足开闭原则。 你以引入在不同类对象上执行的新行为,且无需对这些类做出修改。
  • 为了解决各个模块头文件相互引用问题,这里多次使用C++的前置声明。是一种解决这类问题比较实用的方法。
  • 每增加一个行为要增加多个类,加入这些功能是不需要的,清理起来也是比较复杂的。因此在使用访问者模式前,先审视是否有必要。
  • 访问者模式的实现相对来说很复杂,因为涉及到各个类的双向交互。同时领略访问者的思想,选择恰当的实现方式即可。

http://www.taodudu.cc/news/show-8054146.html

相关文章:

  • [论文评析]AdaptivePose: Human Parts as Adaptive Points,AAAI 2022
  • 一、LinuxC学习总结
  • 让你的「微信小程序」运行在Chrome浏览器上,让我们使用WebStorm
  • 什么是卡式报表,如何制作卡式报表
  • 中控考勤机SDK使用中员工姓名的处理( c# )
  • 第30天学习打卡(异常的概述 IO流概述)
  • 教育行业投放信息广告效果怎样?信息流推广是做什么的?
  • 信息流媒体变现_学会这五点,玩转信息流推广你怕WHO?
  • 看到西蒙的色了,我也小的色下
  • 苹果手机怎么投屏到电视
  • 使用RStudio编辑R语言代码
  • 故宫服饰配色
  • 网站配色杂谈
  • 網頁配色
  • 经济师职称需要英语和计算机吗,经济师考试是否需要考职称英语和职称计算机...
  • 义乌中级职称还用计算机吗,好消息!在义乌评中级职称不用再考职称外语和计算机啦...
  • 职称考试不考英语和计算机,评职称不再考外语和计算机
  • 评职称不需英语计算机的文件,深圳评职称将不需再考职称英语
  • 广东 职称英语计算机,广东职称英语改革:今年起深圳评职称不再考职称英语...
  • Matplotlib 数据可视化详解
  • R:ggplot2(2),第2章 从qplot开始入门(2)
  • Python 的 TOP50 数据可视化 图形(包含代码)
  • html中lbv项目怎么平均分配,css\html布局及部分知识小分享~~~
  • css 属性 clip-path:polygon实现任意图形、多边形
  • 图像处理案例之切除扫描件周边的白边
  • R导出可编辑矢量图
  • 错误总结:使用SQLite时,报错“database is locked”,只能进行select,不能进行delete、update和insert
  • Error from chokidar (D:\): Error: EBUSY: resource busy or locked, lstat ‘D:\pagefile.sys‘
  • Error: EBUSY: resource busy or locked, lstat ‘D:\DumpStack.log.tmp‘
  • package.json 与 package-lock.json你可能不是真得了解
  • 这篇关于S19. 访问者模式的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



    http://www.chinasem.cn/article/276123

    相关文章

    在JS中的设计模式的单例模式、策略模式、代理模式、原型模式浅讲

    1. 单例模式(Singleton Pattern) 确保一个类只有一个实例,并提供一个全局访问点。 示例代码: class Singleton {constructor() {if (Singleton.instance) {return Singleton.instance;}Singleton.instance = this;this.data = [];}addData(value)

    模版方法模式template method

    学习笔记,原文链接 https://refactoringguru.cn/design-patterns/template-method 超类中定义了一个算法的框架, 允许子类在不修改结构的情况下重写算法的特定步骤。 上层接口有默认实现的方法和子类需要自己实现的方法

    【iOS】MVC模式

    MVC模式 MVC模式MVC模式demo MVC模式 MVC模式全称为model(模型)view(视图)controller(控制器),他分为三个不同的层分别负责不同的职责。 View:该层用于存放视图,该层中我们可以对页面及控件进行布局。Model:模型一般都拥有很好的可复用性,在该层中,我们可以统一管理一些数据。Controlller:该层充当一个CPU的功能,即该应用程序

    迭代器模式iterator

    学习笔记,原文链接 https://refactoringguru.cn/design-patterns/iterator 不暴露集合底层表现形式 (列表、 栈和树等) 的情况下遍历集合中所有的元素

    《x86汇编语言:从实模式到保护模式》视频来了

    《x86汇编语言:从实模式到保护模式》视频来了 很多朋友留言,说我的专栏《x86汇编语言:从实模式到保护模式》写得很详细,还有的朋友希望我能写得更细,最好是覆盖全书的所有章节。 毕竟我不是作者,只有作者的解读才是最权威的。 当初我学习这本书的时候,只能靠自己摸索,网上搜不到什么好资源。 如果你正在学这本书或者汇编语言,那你有福气了。 本书作者李忠老师,以此书为蓝本,录制了全套视频。 试

    利用命令模式构建高效的手游后端架构

    在现代手游开发中,后端架构的设计对于支持高并发、快速迭代和复杂游戏逻辑至关重要。命令模式作为一种行为设计模式,可以有效地解耦请求的发起者与接收者,提升系统的可维护性和扩展性。本文将深入探讨如何利用命令模式构建一个强大且灵活的手游后端架构。 1. 命令模式的概念与优势 命令模式通过将请求封装为对象,使得请求的发起者和接收者之间的耦合度降低。这种模式的主要优势包括: 解耦请求发起者与处理者

    springboot实战学习(1)(开发模式与环境)

    目录 一、实战学习的引言 (1)前后端的大致学习模块 (2)后端 (3)前端 二、开发模式 一、实战学习的引言 (1)前后端的大致学习模块 (2)后端 Validation:做参数校验Mybatis:做数据库的操作Redis:做缓存Junit:单元测试项目部署:springboot项目部署相关的知识 (3)前端 Vite:Vue项目的脚手架Router:路由Pina:状态管理Eleme

    状态模式state

    学习笔记,原文链接 https://refactoringguru.cn/design-patterns/state 在一个对象的内部状态变化时改变其行为, 使其看上去就像改变了自身所属的类一样。 在状态模式中,player.getState()获取的是player的当前状态,通常是一个实现了状态接口的对象。 onPlay()是状态模式中定义的一个方法,不同状态下(例如“正在播放”、“暂停

    软件架构模式:5 分钟阅读

    原文: https://orkhanscience.medium.com/software-architecture-patterns-5-mins-read-e9e3c8eb47d2 软件架构模式:5 分钟阅读 当有人潜入软件工程世界时,有一天他需要学习软件架构模式的基础知识。当我刚接触编码时,我不知道从哪里获得简要介绍现有架构模式的资源,这样它就不会太详细和混乱,而是非常抽象和易

    使用Spring Boot集成Spring Data JPA和单例模式构建库存管理系统

    引言 在企业级应用开发中,数据库操作是非常重要的一环。Spring Data JPA提供了一种简化的方式来进行数据库交互,它使得开发者无需编写复杂的JPA代码就可以完成常见的CRUD操作。此外,设计模式如单例模式可以帮助我们更好地管理和控制对象的创建过程,从而提高系统的性能和可维护性。本文将展示如何结合Spring Boot、Spring Data JPA以及单例模式来构建一个基本的库存管理系统