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

实战设计模式之访问者模式

概述

        访问者模式允许我们在不改变类的前提下,向已有类添加新的功能。简单来说,就是将算法与对象的数据结构进行分离的一种方法。在实际应用中,当我们需要对一组对象执行一些操作,而这些操作又需要随着需求的变化而不断变化时,访问者模式就显得尤为重要了。

        电子商务平台的库存管理系统是现实生活中运用访问者模式的一个典型例子。电子商务平台会销售不同种类的商品,比如:书籍、电子产品和服装等。我们需要定期对库存进行不同的统计分析,包括:计算总价值、统计数量等。随着业务的发展,可能会有新的商品类型加入,也可能会有新的统计需求出现。访问者模式允许我们将特定的商品处理逻辑与商品类本身分离,使得添加新的商品类型或处理逻辑变得更为简单和灵活。

基本原理

        访问者模式的核心思想是:将算法与对象结构分离。具体来说,访问者模式通过定义一个操作(通常称为“访问”),这个操作可以在不修改该元素的类的前提下,为每一个具体元素类声明一个该操作。在访问者模式中,我们有两个主要的角色:一个是接受访问的对象集合(即元素),另一个是对这些对象执行操作的访问者。元素知道如何接受访问者,并且会调用访问者的相应方法来完成操作。

        访问者模式主要由以下五个核心组件构成。

        1、访问者。为每一个具体元素声明一个访问操作,表示访问者访问一个元素所要完成的工作。通常情况下,访问者会包含多个Visit方法,每个方法对应一种具体的元素类型。

        2、具体访问者。实现了访问者接口中的每种Visit方法,以完成具体的业务逻辑。

        3、元素。定义了一个接受访问者的接口Accept,其主要作用是让访问者访问自身。同时,Element也是一个抽象类,代表了一组可以被访问的对象。

        4、具体元素。通常是那些需要被访问的对象,实现了Element接口,并提供自身的具体实现。

        5、对象结构。管理元素对象的集合,提供了遍历元素的方法。

        基于上面的核心组件,访问者模式的实现主要有以下五个步骤。

        1、定义元素接口。创建一个抽象类或接口,至少声明了一个Accept方法。该方法用于接收一个访问者对象,并调用访问者的相应Visit方法来执行对当前元素的操作。

        2、创建具体元素类。对于每一个需要被访问的具体元素,创建一个继承自元素接口的类。在每个具体元素类中,实现Accept方法,使其调用传入的访问者对象的对应Visit方法,并传递自身作为参数。

        3、定义访问者接口。创建一个接口,为每种具体访问者类型定义一个Visit方法。这意味着,每当添加一个新的具体访问者类型时,都需要向访问者接口中添加一个新的Visit方法声明。

        4、实现具体访问者类。根据具体的业务逻辑需求,创建实现访问者接口的具体访问者类。在这些类中,实现所有声明的Visit方法,每个方法都包含了针对特定访问者类型的处理逻辑。

        5、创建对象结构。创建一个管理元素集合的对象结构,可以是一个简单的容器,也可以是更复杂的数据结构。此对象结构应提供遍历其内部元素的方法,并能够接受一个访问者对象,依次对其内部的每个元素调用Accept方法。

实战代码

        在下面的实战代码中,我们使用访问者模式模拟了电子商务平台库存管理系统的实现。

        首先,我们定义了一个抽象基类CProduct,它包含商品的基本信息(名称、价格、库存量),并声明了一个纯虚函数Accept用于接收访问者对象。

        接着,我们定义了两个具体的商品类CBook和CElectronics。它们继承自CProduct,并实现了Accept方法。该方法调用传入的访问者的相应Visit方法,以执行针对具体商品类型的特定操作。

        然后,我们定义了一个抽象访问者类CVisitor。其中声明了针对每种商品类型的Visit方法,并通过具体访问者类CStockValueCalculator实现了这些方法,用来计算库存总价值。此外,还有一个管理商品集合的对象结构类CInventory。它负责存储商品实例,并提供了一个PerformCalculations方法遍历所有商品,对每个商品调用其Accept方法,传入具体的访问者对象以执行相应的业务逻辑。

        最后,在main函数中,我们创建了一个CInventory实例。在添加了几种不同类型的商品后,我们使用CStockValueCalculator访问者来计算库存总价值,并最终输出了结果。

#include <iostream>
#include <vector>
#include <string>using namespace std;class CVisitor;// 元素接口
class CProduct
{
public:CProduct(const string& name, double price, int stock) : m_strName(name), m_dbPrice(price), m_nStock(stock) {}virtual ~CProduct() {}virtual void Accept(CVisitor& visitor) = 0;string GetName() const { return m_strName; }double GetPrice() const { return m_dbPrice; }int GetStock() const { return m_nStock; }protected:string m_strName;double m_dbPrice;int m_nStock;
};// 具体元素:书籍
class CBook : public CProduct
{
public:CBook(const string& name, double price, int stock) : CProduct(name, price, stock) {}void Accept(CVisitor& visitor) override;
};// 具体元素:电子产品
class CElectronics : public CProduct
{
public:CElectronics(const string& name, double price, int stock) : CProduct(name, price, stock) {}void Accept(CVisitor& visitor) override;
};// 访问者接口
class CVisitor
{
public:virtual ~CVisitor() {}virtual void Visit(CBook& book) = 0;virtual void Visit(CElectronics& electronics) = 0;
};// 具体访问者:计算库存总价值
class CStockValueCalculator : public CVisitor
{
public:void Visit(CBook& book) override{m_dbTotalValue += book.GetPrice() * book.GetStock();}void Visit(CElectronics& electronics) override{m_dbTotalValue += electronics.GetPrice() * electronics.GetStock();}double GetTotalValue() const { return m_dbTotalValue; }private:double m_dbTotalValue = 0.0;
};void CBook::Accept(CVisitor& visitor)
{visitor.Visit(*this);
}void CElectronics::Accept(CVisitor& visitor)
{visitor.Visit(*this);
}// 对象结构,用于管理商品集合
class CInventory
{
public:~CInventory(){for (CProduct* product : m_vctProduct){delete product;}}void AddProduct(CProduct* product){m_vctProduct.push_back(product);}void PerformCalculations(CVisitor& visitor){for (CProduct* product : m_vctProduct){product->Accept(visitor);}}private:vector<CProduct*> m_vctProduct;
};int main()
{CInventory inventory;inventory.AddProduct(new CBook("Effective C++", 50.0, 10));inventory.AddProduct(new CElectronics("Phone", 1999.99, 20));CStockValueCalculator calculator;inventory.PerformCalculations(calculator);cout << "Total stock value: " << calculator.GetTotalValue() << endl;return 0;
}

总结

        通过将算法从对象结构中分离出来,访问者模式使得每个角色(元素和访问者)都只负责自己的部分。另外,访问者模式使得添加新的操作变得容易,而不需要修改现有的类。只需创建一个新的访问者类来实现所需的操作即可,无需改动已有的元素类。

        但引入访问者模式会增加系统的设计复杂度,特别是当对象结构中有大量不同类型元素时,需要为每种类型定义相应的Visit方法,增加了代码量和理解难度。访问者需要知道所有被访问元素的具体类型和内部表示,这可能会导致访问者与元素之间产生紧密耦合,从而破坏了封装性。

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

相关文章:

  • Javase 基础加强 —— 07 File
  • 云原生安全基石:Linux进程隔离技术详解
  • 2025最新智能优化算法:野燕麦优化算法(Animated Oat Optimization Algorithm, AOO),MATLAB代码
  • JavaSE核心知识点03高级特性03-04(Lambda表达式)
  • 产品迭代与放弃的判断:MVP、PMF 与 Scale Fit 的三重验证
  • VS编码访问Mysql数据库
  • 数据库范式
  • 易贝平台关键字搜索技术深度解析
  • Lesson 21 Mad or not
  • 2024 CKA模拟系统制作 | Step-By-Step | 4、题目搭建-权限控制RBAC
  • 数据库MySQL进阶
  • 【C++】封装红黑树实现 mymap 和 myset
  • 实现Web网站冷启动的全面指南
  • LeetCode 3362.零数组变换 III:贪心+优先队列+差分数组——清晰题解
  • 天猫平台实时商品数据 API 接入方案与开发实践
  • 【时时三省】Python 语言----字符串,列表,元组,字典常用操作异同点
  • Interviews(访谈):业务分析师的“信息开采器”
  • LangGraph 实战指南:长期记忆管理
  • CMSIS-NN:1.简介
  • 【大模型报错解决】cublasLt ran into an error!
  • 开疆智能Profinet转Profibus网关连接DP-IO模块配置案例
  • 水利水电安全员B证职责
  • SpringBoot入门
  • 快速解决Linux 中yum镜像拉取失败问题
  • 算法题(154):合并果子
  • 鸿蒙密码生成器开发笔记
  • C++ 正则表达式简介
  • 广东省省考备考(第十九天5.24)—申论(听课后强化训练)
  • docker虚拟化、容器化
  • 轻量化开源方案——浅析PdfPatcher实际应用