【设计模式深度剖析】【9】【行为型】【访问者模式】| 以博物馆的导览员为例加深理解

👈️上一篇:备忘录模式    |   下一篇:状态模式👉️

设计模式-专栏👈️


文章目录

  • 访问者模式
  • 定义
    • 英文原话
    • 直译
    • 如何理解呢?
  • 访问者模式的角色
    • 类图
    • 代码示例
  • 访问者模式的应用
    • 优点
    • 缺点
    • 使用场景
  • 示例解析:博物馆的导览员
    • 代码示例

访问者模式

访问者模式(Visitor Pattern)就像一位多才多艺的导游,能够根据不同的景点(数据结构中的元素)提供个性化的解说服务(操作),而无需改变景点的本质。

定义

英文原话

The Visitor pattern is a behavioral design pattern that allows you to add new operations to objects without changing their classes. A visitor is a class with methods that correspond to the classes of an object structure it visits. A client uses the visitor to perform the operations on the elements of the object structure.

直译

访问者模式是一种行为设计模式,它允许我们在不改变对象类的情况下为对象添加新的操作。访问者是一个类,它包含的方法对应于它所访问的对象结构中的类。客户端使用访问者来对对象结构的元素执行操作。

如何理解呢?

想象一下我们有一个装满各种形状(圆形、矩形、三角形)的盒子。现在我们想对这些形状进行不同的操作,比如测量它们的面积或周长。使用访问者模式,我们可以创建一个“测量工具”(即访问者),它知道如何与每种形状交互并获取所需的信息,而不需要改变形状本身。

访问者模式的角色

访问者模式中的角色包括:

  1. Visitor(访问者):这是一个接口或抽象类,声明了访问特定元素类时需要执行的操作。
  2. ConcreteVisitor(具体访问者):实现了Visitor接口或继承了Visitor抽象类,实现了对元素类中每个元素的访问操作。
  3. Element(元素):这是一个接口或抽象类,声明了一个接受访问者对象的方法(通常是accept)。
  4. ConcreteElement(具体元素):实现了Element接口或继承了Element抽象类,并实现accept方法以接受访问者的访问。
  5. ObjectStructure(对象结构):能够枚举它的元素,并可以提供一个高层接口以允许访问者访问它的元素。这通常是一个集合类,如列表或树。

类图

在这里插入图片描述

代码示例

package com.polaris.designpattern.list3.behavioral.pattern09.visitor.classicdemo;

import java.util.ArrayList;
import java.util.List;

// Visitor 接口
interface Visitor {
    void visit(ConcreteElementA element);

    void visit(ConcreteElementB element);
}

// ConcreteVisitor 类  
class ConcreteVisitor implements Visitor {
    @Override
    public void visit(ConcreteElementA element) {
        System.out.println("Visiting ConcreteElementA: " + element.operationA());
    }

    @Override
    public void visit(ConcreteElementB element) {
        System.out.println("Visiting ConcreteElementB: " + element.operationB());
    }
}

// Element 接口  
interface Element {
    void accept(Visitor visitor);
}

// ConcreteElementA 类  
class ConcreteElementA implements Element {
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }

    public String operationA() {
        return "elementA info...";
    }
}

// ConcreteElementB 类  
class ConcreteElementB implements Element {
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }

    public String operationB() {
        return "elementB info...";
    }
}

// ObjectStructure 类(这里简单用ArrayList作为示例)

class ObjectStructure {
    private 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 objectStructure = new ObjectStructure();
        objectStructure.add(new ConcreteElementA());
        objectStructure.add(new ConcreteElementB());

        Visitor visitor = new ConcreteVisitor();
        objectStructure.accept(visitor);
    }
}
/* Output:
Visiting ConcreteElementA: elementA info...
Visiting ConcreteElementB: elementB info...
*///~

在这个示例中,我们有一个Visitor接口和两个具体的访问者方法(对应于两种不同类型的元素)。我们还有两个实现了Element接口的ConcreteElement类,它们各自有自己的操作。ObjectStructure类管理了一个Element对象的集合,并提供了一个accept方法来遍历这些对象并接受访问者的访问。在客户端代码中,我们创建了一个ObjectStructure对象,并向其中添加了两个不同类型的元素。然后,我们创建了一个ConcreteVisitor对象,并使用它来访问ObjectStructure中的所有元素。

访问者模式的应用

访问者模式是一种设计模式,它允许在不改变数据结构的前提下,为数据结构中的每个元素定义新的操作。这种模式在需要为数据结构中的元素添加新行为,但又不想修改这些元素所在类的情况下特别有用。

优点

  1. 扩展性好:当需要为数据结构中的元素添加新操作时,只需定义一个新的访问者类即可,无需修改原有类。
  2. 复用性好:访问者模式可以通过为不同的数据结构定义相同的访问者接口,实现操作的复用。
  3. 灵活性好:访问者模式将数据结构与作用于结构上的操作解耦,使得操作集合可以相对自由地演化而不影响系统的数据结构。
  4. 符合单一职责原则:每个访问者只负责一种操作,使代码更加清晰和易于维护。

缺点

  1. 破坏封装:在访问者模式中,具体元素需要向访问者公开其内部状态和方法,这可能会破坏对象的封装性。
  2. 增加新数据结构困难当需要为新的数据结构添加访问者时,需要在访问者接口中添加新的方法,这可能会增加代码的复杂性。
  3. 具体元素变更困难如果具体元素的内部结构发生变化,可能需要修改所有相关的访问者类,这可能会增加维护成本。

使用场景

  1. 电商网站商品分类与操作:电商网站通常有大量的商品,这些商品可以按照不同的属性进行分类。使用访问者模式,可以定义一个访问者对象,它能够访问不同类型的商品对象,并根据需要执行不同的操作,如按照价格排序、根据品牌筛选等。
  2. 图形编辑器的操作处理:在图形编辑器中,有多种图形元素,如线条、圆形、矩形等。访问者模式允许定义一个访问者对象,它能够访问这些图形元素并执行不同的操作,如移动、缩放、旋转或改变颜色等。
  3. 编译器和解释器设计:在编译器或解释器的设计中,抽象语法树(AST)是一个常见的数据结构。使用访问者模式,可以为AST中的不同节点类型定义不同的访问者,以执行如类型检查、代码优化、代码生成等操作。

将源程序表示为一个抽象语法树,编译器需要在抽象语法树上实施某些操作以进行“静态语义分析”,例如检查是否所有的变量都已经被定义了。他也需要生成代码。因此他可能要定义许多操作以进行类型检查、代码优化、流程分析,检查变量是否在使用前被赋初值,等等。此外,还可以使用抽象语法树进行优美格式打印、程序重构、code instrumentation(代码插装,见下面note注释内容)以及对程序进行多种度量。


这些操作大多要求对不同的节点进行不同的处理。例如对代表赋值语句的结点的处理就不同于对代表变量或算术表达式的结点的处理。


因此,有用于赋值语句的类,有用于变量访问的类,还有用于算术表达式的类等等。结点类的集合当然依赖于被编译的语言,但对于一个给定的语言其变化不大(即已有的数据结构变化不大或不变)。

note: Code instrumentation(代码仪器化)是一种软件开发领域的技术。详细解释如下:

  1. 定义
    • Code instrumentation涉及向代码中插入特定的指令或代码片段,以便在程序执行过程中收集各种信息或执行特定的任务。
  2. 用途
    • 调试:通过插入额外的代码,可以更容易地跟踪和调试程序的行为。
    • 性能分析:插入的代码可以收集程序的性能数据,如执行时间、内存使用等。
    • >代码覆盖率分析>:用于确定哪些代码在测试过程中被实际执行过。
    • 安全检查:可以插入用于检测潜在安全问题的代码。
  3. 实现
    • 通过在代码中嵌入仪器化代码,开发人员可以更深入地了解程序的执行过程,并获取关键的运行时信息。
  4. 其他表述
    • Code instrumentation有时也被称为**“代码插装”“代码插桩”**。
  5. 应用实例
    • 在自动化测试和内部质量保证(QA)工具中,code instrumentation被用于基础设施、实施和维护,以及支持代码覆盖率的分析

总结来说,code instrumentation是一种通过向代码中插入特定指令或代码片段来收集信息或执行任务的软件开发技术,它在调试、性能分析、代码覆盖率分析和安全检查等方面发挥着重要作用。

示例解析:博物馆的导览员

假设你是一家博物馆的导览员,博物馆里有很多不同类型的展品,如绘画、雕塑和古代文物。作为导览员,你需要为不同类型的展品提供不同的解说词。但是,展品的种类可能会随着时间增加或减少,而你不希望每次有新的展品加入时都要重新学习所有的解说词。

在这里,展品可以看作是数据结构中的元素,而导览员就是访问者。导览员(访问者)需要访问不同的展品(元素),并为它们提供解说词(操作)。

代码示例

下面是一个简单的Java代码示例,用于模拟这个场景:

package com.polaris.designpattern.list3.behavioral.pattern09.visitor.demo1;

// 展品接口
interface Artifact {
    void accept(TourGuide tourGuide);
}

// 绘画展品  
class Painting implements Artifact {
    private String name;

    public Painting(String name) {
        this.name = name;
    }

    @Override
    public void accept(TourGuide tourGuide) {
        tourGuide.visit(this);
    }

    public String getName() {
        return name;
    }
}

// 雕塑展品  
class Sculpture implements Artifact {
    private String name;

    public Sculpture(String name) {
        this.name = name;
    }

    @Override
    public void accept(TourGuide tourGuide) {
        tourGuide.visit(this);
    }

    public String getName() {
        return name;
    }
}

// 导览员(访问者)接口  
interface TourGuide {
    void visit(Painting painting);

    void visit(Sculpture sculpture);
    // 如果以后有更多类型的展品,可以在这里添加方法  
}

// 具体导览员类  
class EnglishTourGuide implements TourGuide {
    @Override
    public void visit(Painting painting) {
        System.out.println("This is a painting called " + painting.getName() + ". It is beautiful!");
    }

    @Override
    public void visit(Sculpture sculpture) {
        System.out.println("This is a sculpture called " + sculpture.getName() + ". It is impressive!");
    }
}

// 客户端代码  
public class MuseumDemo {
    public static void main(String[] args) {
        // 创建展品  
        Artifact painting = new Painting("Mona Lisa");
        Artifact sculpture = new Sculpture("Thinker");

        // 创建导览员  
        TourGuide tourGuide = new EnglishTourGuide();

        // 导览员访问展品  
        painting.accept(tourGuide);
        sculpture.accept(tourGuide);
    }
}

/* Output:
This is a painting called Mona Lisa. It is beautiful!
This is a sculpture called Thinker. It is impressive!
*///~

在这个示例中,Artifact是展品接口,PaintingSculpture是具体的展品类。TourGuide是导览员接口,EnglishTourGuide是具体的导览员类。


当新的展品类型出现时,只需要实现Artifact接口并添加相应的accept方法,然后在TourGuide接口中添加对应的visit方法即可,而不需要修改已经存在的展品类和导览员类(除了添加新的方法外)。这样,就实现了在不改变数据结构的前提下,为数据结构中的每个元素定义新的操作。

note1: 当系统需要添加新的操作到已有的数据结构(如不同的展品类型)时,我们可以通过增加一个新的访问者类来实现,而无需修改原有的数据结构。

note2:当新的展品类型出现时,需要:

  1. 实现Artifact接口来定义新的展品类(比如Ceramic代表陶瓷展品)。
  2. Artifact接口的实现类(如Ceramic)中,实现accept方法以允许导览员(TourGuide)访问。
  3. 修改TourGuide接口,添加一个新的visit方法来处理新的展品类型(比如void visit(Ceramic ceramic);)。
  4. TourGuide的实现类(如EnglishTourGuide)中,实现新添加的visit方法以提供针对新展品类型的解说词。

这样,当新的展品类型被添加到博物馆时,导览员(TourGuide)就能够处理它,而不需要修改已经存在的展品类和导览员类(除了添加新的方法外)。这种设计使得系统的扩展性很好,能够轻松应对新的展品类型的添加。


👈️上一篇:备忘录模式    |   下一篇:状态模式👉️

设计模式-专栏👈️

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

uni-app与原生插件混合开发调试3-安卓原生插件开发调试和打包

安卓原生插件开发调试和打包 上面已经介绍了怎么安装开发和调试环境&#xff0c;接下来就是安卓原生插件的具体开发和调试步骤&#xff1a; 将uniapp前端项目的index.vue文件新增代码。代码如图所示&#xff1a; <template><view><view><text>{{titl…

GaussDB关键技术原理:高性能(二)

GaussDB关键技术原理&#xff1a;高性能&#xff08;一&#xff09;从数据库性能优化系统概述对GaussDB的高性能技术进行了解读&#xff0c;本篇将从查询处理综述方面继续分享GaussDB的高性能技术的精彩内容。 2 查询处理综述 内容概要&#xff1a;本章节介绍查询端到端处理的…

Go团队的工作方式

在Go 1.23版本[1]即将发布(2024.8)之前&#xff0c;在GopherCon 2024[2]开幕(2024.7)之前&#xff0c;Go团队成员Cameron Balahan(Go产品负责人)[3]、 Sameer Ajmani&#xff08;Go团队工程总监&#xff09;和Russ Cox&#xff08;Go团队技术负责人&#xff09;[4]参加了业界知…

2024 年最新 Python 基于火山引擎豆包大模型搭建 QQ 机器人详细教程(更新中)

豆包大模型概述 火山引擎官网&#xff1a;https://www.volcengine.com/ 字节跳动推出的自研大模型。通过字节跳动内部50业务场景实践验证&#xff0c;每日千亿级tokens大使用量持续打磨&#xff0c;提供多模态能力&#xff0c;以优质模型效果为企业打造丰富的业务体验。 模型…

“论云原生架构及其应用”写作框架,系统架构设计师

论文真题 近年来&#xff0c;随着数字化转型不断深入&#xff0c;科技创新与业务发展不断融合&#xff0c;各行各业正在从大工业时代的固化范式进化成面向创新型组织与灵活型业务的崭新模式。在这一背景下&#xff0c;以容器和微服务架构为代表的云原生技术作为云计算服务的新…

智能化改造助力企业高质量发展

引言 背景介绍 在当今全球经济环境中&#xff0c;变化和不确定性已成为常态。企业面临的竞争压力不断增加&#xff0c;市场竞争日益激烈。新兴市场的崛起、技术进步和消费者需求的快速变化&#xff0c;使得传统的商业模式和生产方式面临巨大挑战。为了在这样的环境中保持竞争力…

Go语言之基础入门

网站&#xff1a;http://hardyfish.top/ 免费书籍分享&#xff1a; 资料链接&#xff1a;https://url81.ctfile.com/d/57345181-61545511-81795b?p3899 访问密码&#xff1a;3899 免费专栏分享&#xff1a; MySQL是怎样运行的从根儿上理解MySQL 课程链接&#xff1a;https:/…

(13)DroneCAN 适配器节点(一)

文章目录 前言 1 特点 2 固件 3 ArduPilot固件DroneCAN设置 4 DroneCAN适配器节点 前言 这些节点允许现有的 ArduPilot 支持的外围设备作为 DroneCAN 或 MSP 设备适应 CAN 总线。这也允许扩展自动驾驶仪硬件的功能。如允许 I2C 设备&#xff08;如罗盘或空速&#xff09…

网信办公布第六批深度合成服务算法备案清单,深兰科技大模型入选

6月12日&#xff0c;国家互联网信息办公室发布了第六批深度合成服务算法备案信息&#xff0c;深兰科技硅基知识智能对话多模态大模型算法通过相关审核&#xff0c;成功入选该批次《境内深度合成服务算法备案清单》。同时入选的还有腾讯混元大模型多模态算法、支付宝图像生成算法…

什么样的落地台灯比较好?五款宝藏可靠护眼大路灯推荐

现代家庭中&#xff0c;落地台灯也逐渐的代替传统台灯&#xff0c;成为许多孩子在读写时的照明神器&#xff0c;它已经被许多家长认可&#xff0c;宽广的光线光线清晰&#xff0c;视觉上舒适了不少。然而&#xff0c;目前市场上有许多品牌未经过充分的技术、材质和工艺研究&…

OpenCV颜色检测

OpenCV颜色检测 前言策略分析根据颜色检测目标对象相关链接 前言 绿幕技术是一种经典的视频编辑技术&#xff0c;可以用于将人物置于不同的背景中。例如在电影制作中&#xff0c;技术的关键在于演员不能身着特定颜色的衣服(比如绿色)&#xff0c;站在只有绿色的背景前。然后&a…

【Nginx】Nginx安装及简单使用

https://www.bilibili.com/video/BV1F5411J7vK https://www.kuangstudy.com/bbs/1353634800149213186 https://stonecoding.net/system/nginx/nginx.html https://blog.csdn.net/qq_40492693/article/details/124453090 Nginx 是一个高性能的 HTTP 和反向代理 Web 服务器。其特…

Benchmarking Panoptic Scene Graph Generation (PSG), ECCV‘22 场景图生成,利用PSG数据集

2080-ti显卡复现 源代码地址 Jingkang50/OpenPSG: Benchmarking Panoptic Scene Graph Generation (PSG), ECCV22 (github.com) 安装 pytorch 1.7版本 cuda10.1 按照readme的做法安装 我安装的过程如下图所示,这个截图是到了pip install openmim这一步 下一步 下一步 这一步…

【八】【QT开发应用】QTcreate项目打包成.exe文件或.apk文件,EnigmaVirtualBox软件下载,虚拟网站代打开QT应用

EnigmaVirtualBox下载 Enigma Virtual Box QTcreate项目打包成.exe可执行文件 找到自己写好的项目的.exe文件 将这个文件复制到一个新的文件夹里面 在这个新的文件夹里面打开cmd,这样可以使得cmd直接进入到该文件夹 打包.exe命令行 输入下面的命令行 windeployqt game…

写一个坏越的个人天地(二)

小红书上搜了下博客,感觉好像没有让自己喜欢的。昨天刚好学了点grid布局,来试试 菜单栏直接使用el-menu 下边布局就用grid局部了,这块初步想法是轮播+你的天气和我的天气+自我介绍 天气的话,这边要先找一下有没有天气的api 我这边百度搜了个聚合的api,一天可以免费调用5…

u盘sd卡格式化怎么恢复,3种恢复方法教学

u盘sd卡格式化怎么恢复&#xff0c;这是许多人在误操作后最关心的问题。我们会详细介绍五种有效的恢复方法&#xff0c;并且提供恢复原理的教学视频&#xff0c;帮助您轻松找回U盘和SD卡上被格式化的数据。 一. 数据存储与恢复的原理 1. U盘、移动硬盘、硬盘以及固态盘存储数据…

晶谷电子器件烧结封装介质材料 绝缘用晶谷低温封接环保玻璃粉 耐压高

电子器件烧结封装介质材料是用于保护和封装电子器件的关键材料。 常见的电子器件烧结封装介质材料包括以下几种&#xff1a; 1. 陶瓷材料&#xff1a;具有良好的绝缘性能、耐高温性能和机械强度。 2. 高分子材料&#xff1a;如环氧树脂等&#xff0c;具有良好的柔韧性和耐湿…

安卓手机autox.js连接电脑vscode服务

开通电脑防火墙 好多提示需要将端口通过防火墙放行&#xff0c;没有交怎么放行的。按照下方入站和出站规则都添加好的话就可以放行端口。 启动adb 本地中找到adb的目录&#xff0c;使用下方的两个命令&#xff0c;开启和结束。 adb start-server adb kill-server开启开发者…

电脑网络动态IP配置:步骤详解与实用指南

在构建和管理电脑网络时&#xff0c;IP地址的配置是一个关键步骤。IP地址是设备在网络中的唯一标识符&#xff0c;它决定了设备如何与其他设备进行通信。有两种主要的IP地址类型&#xff1a;静态IP和动态IP。静态IP是手动配置的&#xff0c;而动态IP则是由网络中的DHCP服务器自…

上海媒体邀约的类型有哪些?分行业精准邀约

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 媒体宣传加速季&#xff0c;100万补贴享不停&#xff0c;一手媒体资源&#xff0c;全国100城线下落地执行。详情请联系胡老师。 上海作为中国最大的城市之一&#xff0c;拥有丰富的媒体资…