23种设计模式-访问者(Visitor)设计模式

news2024/11/19 20:49:08

文章目录

  • 一.什么是访问者模式?
  • 二.访问者模式的结构
  • 三.访问者模式的应用场景
  • 四.访问者模式的优缺点
  • 五.访问者模式的C++实现
  • 六.访问者模式的JAVA实现
  • 七.代码解释
  • 八.总结

类图: 访问者设计模式类图

一.什么是访问者模式?

访问者模式(Visitor Pattern)是一种行为型设计模式,允许在不更改元素类的情况下,为对象结构中的元素增加新的操作。访问者模式通过将操作从元素类中抽离出来,实现操作的扩展。
 在访问者模式中,核心思想是将数据结构和操作分开,数据结构负责提供必要的接口,而具体的操作逻辑由访问者实现。

二.访问者模式的结构

  • Visitor(访问者接口):声明一组访问方法,每个方法对应一种具体的元素类型。
  • ConcreteVisitor(具体访问者):实现访问者接口中声明的操作,对不同的元素提供不同的处理。
  • Element(元素接口):定义一个Accept方法,用于接收访问者对象。
  • ConcreteElement(具体元素):实现元素接口,并在Accept方法中调用访问者的相应方法。
  • ObjectStructure(对象结构):提供元素的容器,负责存储和遍历元素。
    在这里插入图片描述

三.访问者模式的应用场景

  1. 数据结构稳定,操作逻辑需要频繁变化:例如编译器中的语法树遍历。
  2. 需要对一组不相关的类执行操作:如不同形状的图形对象(圆形、矩形等)。
  3. 需要集中管理行为,而不想污染元素类

四.访问者模式的优缺点

  • 优点
    • 增加新的操作很方便,只需新增一个具体访问者类。
    • 遵循单一职责原则,将操作与元素本身解耦。
  • 缺点
    • 增加新的元素类型需要修改所有访问者,违背了开闭原则。
    • 对象结构不容易扩展。

五.访问者模式的C++实现

#include <iostream>
#include <vector>
#include <memory>

// 前置声明,避免循环引用
class ConcreteElementA;
class ConcreteElementB;
class Visitor;

// 访问者接口(Visitor)
class Visitor {
public:
    virtual void visitConcreteElementA(ConcreteElementA& element) = 0;
    virtual void visitConcreteElementB(ConcreteElementB& element) = 0;
    virtual ~Visitor() = default;
};

// 元素接口(Element)
class Element {
public:
    virtual void accept(std::shared_ptr<Visitor> visitor) = 0;
    virtual ~Element() = default;
};

// 具体元素 A(ConcreteElementA)
class ConcreteElementA : public Element {
public:
    void accept(std::shared_ptr<Visitor> visitor) override {
        visitor->visitConcreteElementA(*this);
    }

    void operationA() {
        std::cout << "ConcreteElementA specific operation.\n";
    }
};

// 具体元素 B(ConcreteElementB)
class ConcreteElementB : public Element {
public:
    void accept(std::shared_ptr<Visitor> visitor) override {
        visitor->visitConcreteElementB(*this);
    }

    void operationB() {
        std::cout << "ConcreteElementB specific operation.\n";
    }
};

// 具体访问者 1(ConcreteVisitor1)
class ConcreteVisitor1 : public Visitor {
public:
    void visitConcreteElementA(ConcreteElementA& element) override {
        std::cout << "ConcreteVisitor1 visiting ConcreteElementA.\n";
        element.operationA();
    }

    void visitConcreteElementB(ConcreteElementB& element) override {
        std::cout << "ConcreteVisitor1 visiting ConcreteElementB.\n";
        element.operationB();
    }
};

// 具体访问者 2(ConcreteVisitor2)
class ConcreteVisitor2 : public Visitor {
public:
    void visitConcreteElementA(ConcreteElementA& element) override {
        std::cout << "ConcreteVisitor2 visiting ConcreteElementA.\n";
        element.operationA();
    }

    void visitConcreteElementB(ConcreteElementB& element) override {
        std::cout << "ConcreteVisitor2 visiting ConcreteElementB.\n";
        element.operationB();
    }
};

// 对象结构(ObjectStructure)
class ObjectStructure {
private:
    std::vector<std::shared_ptr<Element>> elements;

public:
    void addElement(std::shared_ptr<Element> element) {
        elements.push_back(element);
    }

    void accept(std::shared_ptr<Visitor> visitor) {
        for (auto& element : elements) {
            element->accept(visitor);
        }
    }
};

// 测试代码
int main() {
    // 创建对象结构
    ObjectStructure objectStructure;

    // 添加元素
    objectStructure.addElement(std::make_shared<ConcreteElementA>());
    objectStructure.addElement(std::make_shared<ConcreteElementB>());

    // 创建访问者 1 并访问
    std::shared_ptr<Visitor> visitor1 = std::make_shared<ConcreteVisitor1>();
    std::cout << "Using ConcreteVisitor1:\n";
    objectStructure.accept(visitor1);

    // 创建访问者 2 并访问
    std::shared_ptr<Visitor> visitor2 = std::make_shared<ConcreteVisitor2>();
    std::cout << "\nUsing ConcreteVisitor2:\n";
    objectStructure.accept(visitor2);

    return 0;
}

六.访问者模式的JAVA实现

// 抽象访问者
interface Visitor {
    void VisitConcreteElementA(ConcreteElementA elementA);
    void VisitConcreteElementB(ConcreteElementB elementB);
}

// 具体访问者1
class ConcreteVisitor1 implements Visitor {
    @Override
    public void VisitConcreteElementA(ConcreteElementA elementA) {
        System.out.println("ConcreteVisitor1 visiting ConcreteElementA");
    }

    @Override
    public void VisitConcreteElementB(ConcreteElementB elementB) {
        System.out.println("ConcreteVisitor1 visiting ConcreteElementB");
    }
}

// 具体访问者2
class ConcreteVisitor2 implements Visitor {
    @Override
    public void VisitConcreteElementA(ConcreteElementA elementA) {
        System.out.println("ConcreteVisitor2 visiting ConcreteElementA");
    }

    @Override
    public void VisitConcreteElementB(ConcreteElementB elementB) {
        System.out.println("ConcreteVisitor2 visiting ConcreteElementB");
    }
}

// 抽象元素
abstract class Element {
    public abstract void Accept(Visitor visitor);
}

// 具体元素A
class ConcreteElementA extends Element {
    @Override
    public void Accept(Visitor visitor) {
        visitor.VisitConcreteElementA(this);
    }

    public void OperationA() {
        System.out.println("ConcreteElementA specific operation.");
    }
}

// 具体元素B
class ConcreteElementB extends Element {
    @Override
    public void Accept(Visitor visitor) {
        visitor.VisitConcreteElementB(this);
    }

    public void OperationB() {
        System.out.println("ConcreteElementB specific operation.");
    }
}

// 对象结构
class ObjectStructure {
    private final List<Element> elements = new ArrayList<>();

    public void Add(Element element) {
        elements.add(element);
    }

    public void Accept(Visitor visitor) {
        for (Element element : elements) {
            element.Accept(visitor);
        }
    }
}

// 客户端代码
public class VisitorPatternDemo {
    public static void main(String[] args) {
        // 创建对象结构
        ObjectStructure structure = new ObjectStructure();

        // 添加具体元素
        structure.Add(new ConcreteElementA());
        structure.Add(new ConcreteElementB());

        // 创建并使用访问者1
        ConcreteVisitor1 visitor1 = new ConcreteVisitor1();
        structure.Accept(visitor1);

        // 创建并使用访问者2
        ConcreteVisitor2 visitor2 = new ConcreteVisitor2();
        structure.Accept(visitor2);
    }
}

七.代码解释

  1. Element 接口和 ConcreteElement
    • Element接口定义了一个accept方法,具体元素类需要实现该方法,并在其中调用访问者的相应方法。
  2. Visitor 接口和 ConcreteVisitor
    • Visitor接口声明了针对每种具体元素的访问方法。
    • ConcreteVisitor1和ConcreteVisitor2实现了Visitor接口,并定义了对ConcreteElementA和ConcreteElementB的具体操作。
  3. ObjectStructure 类
    • ObjectStructure维护了一个元素集合,并提供accept方法,用于将访问者传递给集合中的每个元素。
  4. 测试代码
    • 创建了一个对象结构,并添加了两种具体元素。
    • 使用两个不同的访问者访问元素集合,展示了访问者模式的扩展性。

八.总结

访问者模式是一种将操作和数据结构分离的强大工具。当需要频繁添加新的操作或行为时,它提供了扩展性良好的解决方案。通过访问者模式,可以大幅简化复杂操作的管理,提升代码的可读性和维护性

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2243613.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JavaScript——DOM编程、JS的对象和JSON

一、DOM编程 DOM(Document Object Model)编程&#xff1a;就是使用document对象的API&#xff0c;完成对网页HTML文档进行动态修改&#xff0c;以实现网页数据&#xff0c;和样式动态变化效果的编程。 (一)DOM获取元素的多种方法 1.查找元素的函数 getElementById("id值…

Pr:音频过渡

Adobe Premiere Pro 自带一组共三个音频过渡 Audio Transitions效果。 对音频剪辑之间应用交叉淡化 Crossfade过渡&#xff0c;操作方式类似于应用视频过渡效果。 对于交叉淡化&#xff0c;要保证前剪辑的出点之后及后剪辑的入点之前有足够的预留内容&#xff08;也称“手柄”&…

大数据-226 离线数仓 - Flume 优化配置 自定义拦截器 拦截原理 拦截器实现 Java

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; Java篇开始了&#xff01; 目前开始更新 MyBatis&#xff0c;一起深入浅出&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff0…

stm32启动过程解析startup启动文件

1.STM32的启动过程模式 1.1 根据boot引脚决定三种启动模式 复位后&#xff0c;在 SYSCLK 的第四个上升沿锁存 BOOT 引脚的值。BOOT0 为专用引脚&#xff0c;而 BOOT1 则与 GPIO 引脚共用。一旦完成对 BOOT1 的采样&#xff0c;相应 GPIO 引脚即进入空闲状态&#xff0c;可用于…

如何在项目中用elementui实现分页器功能

1.在结构部分复制官网代码&#xff1a; <template> 标签: 这是 Vue 模板的根标签&#xff0c;包含所有的 HTML 元素和 Vue 组件。 <div> 标签: 这是一个普通的 HTML 元素&#xff0c;包裹了 el-pagination 组件。它没有特别的意义&#xff0c;只是为了确保 el-pagi…

15-大模型 RAG 经验篇

一、LLMs 已经具备了较强能力了&#xff0c;存在哪些不足点? 在 LLM 已经具备了较强能力的基础上&#xff0c;仍然存在以下问题&#xff1a; 幻觉问题&#xff1a;LLM 文本生成的底层原理是基于概率的 token by token 的形式&#xff0c;因此会不可避免地产生"一本正经…

数据结构-二叉树及其遍历

🚀欢迎来到我的【数据结构】专栏🚀 🙋我是小蜗,一名在职牛马。🐒我的博客主页​​​​​​ ➡️ ➡️ 小蜗向前冲的主页🙏🙏欢迎大家的关注,你们的关注是我创作的最大动力🙏🙏🌍前言 本篇文章咱们聊聊数据结构中的树,准确的说因该是只说一说二叉树以及相…

Iview DatePicker 仅允许选择当前月份及以后的月份

iview DatePicker之前月份禁用且下月可用 html代码 <DatePicker type"month" :options"options4" :value"dialogForm.estimatedStartTimeWithCreate" on-change"monthTime($event, loadDateStart)" placeholder"请选择时间&q…

r-and-r——提高长文本质量保证任务的准确性重新提示和上下文搜索的新方法可减轻大规模语言模型中的迷失在中间现象

概述 随着大规模语言模型的兴起&#xff0c;自然语言处理领域取得了重大发展。这些创新的模型允许用户通过输入简单的 "提示 "文本来执行各种任务。然而&#xff0c;众所周知&#xff0c;在问题解答&#xff08;QA&#xff09;任务中&#xff0c;用户在处理长文本时…

【GPTs】Ai-Ming:AI命理助手,个人运势与未来发展剖析

博客主页&#xff1a; [小ᶻZ࿆] 本文专栏: AIGC | GPTs应用实例 文章目录 &#x1f4af;GPTs指令&#x1f4af;前言&#x1f4af;Ai-Ming主要功能适用场景优点缺点 &#x1f4af;小结 &#x1f4af;GPTs指令 中文翻译&#xff1a; defcomplete_sexagenary&#xff08;年&a…

ubuntu24.04网卡配置

vim /etc/netplan/01-netcfg.yaml /24表示子网掩码的长度。这种表示法称为CIDR&#xff08;无类别域间路由&#xff09;记法。CIDR记法将IP地址和它们的子网掩码合并为一个单一的值&#xff0c;其中斜杠/后面的数字表示子网掩码中连续的1的位数。 对于/24&#xff1a; 24表示…

【linux】如何扩展磁盘容量(VMware虚拟机)-转载

如何扩展磁盘容量(VMware虚拟机) 一、前置准备工作 扩展虚拟机磁盘前&#xff0c;需要先把虚拟机关机才能进行扩展磁盘操作 1.选择虚拟机设置&#xff0c;如下图所示 2.输入你想扩展的磁盘容量&#xff0c;以本次实操为例&#xff0c;我这里输入的30G&#xff08;具体按照实…

python 数据类型----可变数据类型

一、list列表类型&#xff1a; 一种有序集合&#xff0c;里面有多个数据用逗号隔开&#xff0c;可以对数据进行追加、插入、删除和替换&#xff1b;使用[]标识&#xff0c;可以包含任意数据类型 登录后复制 # 字符串类型列表 names[bill,may,jack]#整数型列表 numbers [1,2,34…

STARTS:一种用于自动脑电/脑磁(E/MEG)源成像的自适应时空框架|文献速递-基于深度学习的病灶分割与数据超分辨率

Title 题目 STARTS: A Self-adapted Spatio-temporal Framework for Automatic E/MEG SourceImaging STARTS&#xff1a;一种用于自动脑电/脑磁(E/MEG)源成像的自适应时空框架 01 文献速递介绍 电生理源成像&#xff08;Electrophysiological Source Imaging&#xff0c;E…

海康威视和大华视频设备对接方案

目录 一、海康威视 【老版本】 【新版本】 二、大华 一、海康威视 【老版本】 URL规定&#xff1a; rtsp://username:password[ipaddress]/[videotype]/ch[number]/[streamtype] 注&#xff1a;VLC可以支持解析URL里的用户名密码&#xff0c;实际发给设备的RTSP请求不支…

20.UE5UI预构造,开始菜单,事件分发器

2-22 开始菜单、事件分发器、UI预构造_哔哩哔哩_bilibili 目录 1.UI预构造 2.开始菜单和开始关卡 2.1开始菜单 2.2开始关卡 2.3将开始菜单展示到开始关卡 3.事件分发器 1.UI预构造 如果我们直接再画布上设计我们的按钮&#xff0c;我们需要为每一个按钮进行编辑&#x…

手搓神经网络(MLP)解决MNIST手写数字识别问题 | 数学推导+代码实现 | 仅用numpy,tensor和torch基本计算 | 含正反向传播数学推导

手写数字识别&#xff08;神经网络入门&#xff09; 文章目录 手写数字识别&#xff08;神经网络入门&#xff09;实验概述实验过程数据准备模型实现线性变换层前向传播反向传播更新参数整体实现 激活函数层&#xff08;ReLU&#xff09;前向传播反向传播整体实现 Softmax层&am…

极速入门数模电路

一. 认识数模元器件 1.1 面包板 1.2 导线 一般使用红色导线表示正极&#xff0c;黑色导线表示负极。 1.3 纽扣电池 1.4 电池座 1.4 LED灯 1.5 数码管 1.6 有源蜂鸣器 1.7 扬声器 1.8 电容 电容接电池之后可以充电&#xff0c;充完电后电容接LED灯可以放电。 1.9 电阻 1.1…

Windows docker下载minio出现“Using default tag: latestError response from daemon”

Windows docker下载minio出现 Using default tag: latest Error response from daemon: Get "https://registry-1.docker.io/v2/": context deadline exceeded 此类情况&#xff0c;一般为镜像地址问题。 {"registry-mirrors": ["https://docker.re…

使用MaxKB搭建知识库问答系统并接入个人网站(halo)

首发地址&#xff08;欢迎大家访问&#xff09;&#xff1a;使用MaxKB搭建知识库问答系统并接入个人网站 前言 从OpenAI推出ChatGPT到现在&#xff0c;大模型已经渗透到各行各业&#xff0c;大模型也逐渐趋于平民化&#xff1b;从最开始对其理解、生成、强大的知识积累的惊叹&…