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

    相关文章

    Java设计模式---迭代器模式(Iterator)解读

    《Java设计模式---迭代器模式(Iterator)解读》:本文主要介绍Java设计模式---迭代器模式(Iterator),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,... 目录1、迭代器(Iterator)1.1、结构1.2、常用方法1.3、本质1、解耦集合与遍历逻辑2、统一

    Java 线程安全与 volatile与单例模式问题及解决方案

    《Java线程安全与volatile与单例模式问题及解决方案》文章主要讲解线程安全问题的五个成因(调度随机、变量修改、非原子操作、内存可见性、指令重排序)及解决方案,强调使用volatile关键字... 目录什么是线程安全线程安全问题的产生与解决方案线程的调度是随机的多个线程对同一个变量进行修改线程的修改操

    Redis Cluster模式配置

    《RedisCluster模式配置》:本文主要介绍RedisCluster模式配置,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录分片 一、分片的本质与核心价值二、分片实现方案对比 ‌三、分片算法详解1. ‌范围分片(顺序分片)‌2. ‌哈希分片3. ‌虚

    RabbitMQ工作模式中的RPC通信模式详解

    《RabbitMQ工作模式中的RPC通信模式详解》在RabbitMQ中,RPC模式通过消息队列实现远程调用功能,这篇文章给大家介绍RabbitMQ工作模式之RPC通信模式,感兴趣的朋友一起看看吧... 目录RPC通信模式概述工作流程代码案例引入依赖常量类编写客户端代码编写服务端代码RPC通信模式概述在R

    SQL Server身份验证模式步骤和示例代码

    《SQLServer身份验证模式步骤和示例代码》SQLServer是一个广泛使用的关系数据库管理系统,通常使用两种身份验证模式:Windows身份验证和SQLServer身份验证,本文将详细介绍身份... 目录身份验证方式的概念更改身份验证方式的步骤方法一:使用SQL Server Management S

    Redis高可用-主从复制、哨兵模式与集群模式详解

    《Redis高可用-主从复制、哨兵模式与集群模式详解》:本文主要介绍Redis高可用-主从复制、哨兵模式与集群模式的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝... 目录Redis高可用-主从复制、哨兵模式与集群模式概要一、主从复制(Master-Slave Repli

    一文带你搞懂Redis Stream的6种消息处理模式

    《一文带你搞懂RedisStream的6种消息处理模式》Redis5.0版本引入的Stream数据类型,为Redis生态带来了强大而灵活的消息队列功能,本文将为大家详细介绍RedisStream的6... 目录1. 简单消费模式(Simple Consumption)基本概念核心命令实现示例使用场景优缺点2

    Nginx location匹配模式与规则详解

    《Nginxlocation匹配模式与规则详解》:本文主要介绍Nginxlocation匹配模式与规则,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、环境二、匹配模式1. 精准模式2. 前缀模式(不继续匹配正则)3. 前缀模式(继续匹配正则)4. 正则模式(大

    Linux系统配置NAT网络模式的详细步骤(附图文)

    《Linux系统配置NAT网络模式的详细步骤(附图文)》本文详细指导如何在VMware环境下配置NAT网络模式,包括设置主机和虚拟机的IP地址、网关,以及针对Linux和Windows系统的具体步骤,... 目录一、配置NAT网络模式二、设置虚拟机交换机网关2.1 打开虚拟机2.2 管理员授权2.3 设置子

    SpringBoot如何通过Map实现策略模式

    《SpringBoot如何通过Map实现策略模式》策略模式是一种行为设计模式,它允许在运行时选择算法的行为,在Spring框架中,我们可以利用@Resource注解和Map集合来优雅地实现策略模式,这... 目录前言底层机制解析Spring的集合类型自动装配@Resource注解的行为实现原理使用直接使用M