技术成神之路:设计模式(十三)访问者模式

embedded/2024/11/15 4:03:07/

介绍

访问者模式(Visitor Pattern)是一种行为型设计模式,它允许你在不改变对象结构的前提下,定义作用于这些对象的新操作。这种模式通过将操作逻辑从对象结构中抽离出来,使得新的操作可以无缝地添加到现有对象中。

1.定义


访问者模式定义了一个访问者接口,它包含了访问不同元素的操作方法。具体的元素类接受访问者并调用相应的访问方法。通过这种分离,新增的操作可以直接通过访问者来实现,而不需要修改元素类。

2. 主要作用


  • 允许在不修改元素类的情况下,定义新的操作。
  • 提高了系统的可扩展性。
  • 避免了对复杂对象结构的直接修改。

3. 解决的问题


访问者模式解决了如何在不修改对象结构的情况下,向对象添加新的操作的问题。尤其在系统需要频繁添加新操作时,显得尤为重要。

4. 模式原理


包含角色:

  1. Visitor:定义了对各类元素对象的操作接口。
  2. ConcreteVisitor:实现Visitor接口,具体实现访问操作。
  3. Element:定义接受访问者的方法accept(), 通常由具体元素实现。
  4. ConcreteElement:实现Element接口,实现accept()方法。
  5. ObjectStructure:维护Element对象的集合,并提供遍历功能。

看到这么多的角色,就知道访问者模式并不简单,毕竟它是 《设计模式》中较为复杂的一个。不用怕,先以了解为主,因为它在实际开发中毕竟不常用。

UML类图:
在这里插入图片描述

示例:
假设我们有一个表示不同形状(如圆形和矩形)的对象结构,我们希望对这些形状执行不同的操作(如计算面积和绘制形状)。

定义形状接口和具体形状类:

// Shape接口
interface Shape {void accept(Visitor visitor);
}// 圆形类
class Circle implements Shape {double radius;Circle(double radius) {this.radius = radius;}public double getRadius() {return radius;}@Overridepublic void accept(Visitor visitor) {visitor.visit(this);}
}// 矩形类
class Rectangle implements Shape {double width;double height;Rectangle(double width, double height) {this.width = width;this.height = height;}public double getWidth() {return width;}public double getHeight() {return height;}@Overridepublic void accept(Visitor visitor) {visitor.visit(this);}
}

定义访问者接口和具体访问者类:

// 访问者接口
interface Visitor {void visit(Circle circle);void visit(Rectangle rectangle);
}// 具体访问者类 - 计算面积
class AreaVisitor implements Visitor {@Overridepublic void visit(Circle circle) {double area = Math.PI * circle.getRadius() * circle.getRadius();System.out.println("Circle area: " + area);}@Overridepublic void visit(Rectangle rectangle) {double area = rectangle.getWidth() * rectangle.getHeight();System.out.println("Rectangle area: " + area);}
}// 具体访问者类 - 绘制形状
class DrawVisitor implements Visitor {@Overridepublic void visit(Circle circle) {System.out.println("Drawing a Circle with radius " + circle.getRadius());}@Overridepublic void visit(Rectangle rectangle) {System.out.println("Drawing a Rectangle with width " + rectangle.getWidth() + " and height " + rectangle.getHeight());}
}

使用访问者模式

public class VisitorPatternDemo {public static void main(String[] args) {Shape circle = new Circle(5);Shape rectangle = new Rectangle(4, 6);Visitor areaVisitor = new AreaVisitor();Visitor drawVisitor = new DrawVisitor();System.out.println("Calculating areas:");circle.accept(areaVisitor);rectangle.accept(areaVisitor);System.out.println("\nDrawing shapes:");circle.accept(drawVisitor);rectangle.accept(drawVisitor);}
}

打印输出:

Calculating areas:
Circle area: 78.53981633974483
Rectangle area: 24.0Drawing shapes:
Drawing a Circle with radius 5.0
Drawing a Rectangle with width 4.0 and height 6.0

说真的,写到这里我还是懵懵的,但隐约感觉这个设计不错,哈哈哈。

举个栗子,假如你是个想了解学生信息的老师(Visitor),你可以是班主任也可以是体育老师(ConcreteVisitor),因为他们的关注点不同(例如,班主任关注学生的学习成绩,体育老师关注学生的运动能力),学校里的不同类型的学生(Element),具体的学生(ConcreteElement)(例如文艺生A,运动生B)。

此时,假设我们有一个学校系统,其中有多种学生类型和评价标准。

  • 学生接口(Student):定义一个 accept(Visitor visitor) 方法。
  • 具体学生(SpecificStudent):实现 Student 接口,能够接受不同的访问者。
  • 访问者接口(Visitor):定义访问不同学生类型的方法,如 visit(ArtStudent artStudent)visit(SportsStudent sportsStudent)
  • 具体访问者(ConcreteVisitor):实现具体的访问逻辑,比如 AcademicEvaluator 关注学术成绩,PhysicalEducationEvaluator 关注体育表现。

回头再看一遍定义,访问者模式定义了一个访问者接口,它包含了访问不同元素的操作方法,此处的不同元素就是不同的学生,具体的元素类接受访问者并调用相应的访问方法,此处的相应的访问方法就是 学术成绩 和 体育表现,通过这种方式,可以在不改变学生对象结构的前提下,增加新的评价逻辑或操作,而不需要修改学生类本身。

到这里,是不是更进一步了解访问者模式了,不明白也不要紧,记住定义就行了。我突然想到我高中数学老师说的一句话:别管为啥这样写,你就记住,这样写就给分!哈哈。有时候不必太纠结,现在不明白 后面你也会明白的😉

5. 优缺点


优点:

  1. 扩展性强:新增功能时无需改动现有类。
  2. 操作集中:相关操作集中在访问者中,易于维护。

缺点:

  1. 增加复杂性:增加了类的数量,可能使系统变得复杂。
  2. 难以扩展元素:如果需要增加新的元素类,则需要修改访问者接口和所有具体访问者。

6. 应用场景


  • 当需要对一组对象进行不同的操作而又不希望修改这些对象时。
  • 需要对对象结构进行多种操作而操作逻辑复杂的情况下。
  • 系统中的对象结构比较复杂且稳定,而操作经常变化时。

7. 总结


访问者模式是一种强大的设计模式,可以方便地向对象结构中添加新的操作,而无需修改对象结构本身。然而,这种模式也有其局限性,主要在于它要求对象结构相对稳定,在对象结构频繁变化的系统中使用会增加维护成本。因此,在使用访问者模式时,需要根据具体情况权衡其优缺点,选择合适的应用场景。


http://www.ppmy.cn/embedded/113493.html

相关文章

Unity URP APK打包物体不渲染问题

在测试Shader性能的时候,打包到真机上测试是不可少的。但在一次打包APK时安装,打开程序竟然发现本应该生成的物体都不渲染了,但是在Debug的输出UI上确确实实生成了固定数量的物体,而它们的MeshRender却没有任何渲染,但…

TASK-CUSTOMIZEDMASKED AUTOENCODERVIA MIXTURE OF CLUSTER-CONDITIONAL EXPERTS

发表于:ICLR 2023 notable top 25%(相当于spotlight) 推荐指数: #paper/⭐⭐⭐ 论文链接: Task-customized Masked Autoencoder via Mixture of Cluster-conditional Experts | OpenReview poster链接:ICLR 2023 Task-customized Masked Auto…

Day26_0.1基础学习MATLAB学习小技巧总结(26)——数据插值

利用空闲时间把碎片化的MATLAB知识重新系统的学习一遍,为了在这个过程中加深印象,也为了能够有所足迹,我会把自己的学习总结发在专栏中,以便学习交流。 参考书目: 1、《MATLAB基础教程 (第三版) (薛山)》 2、《MATL…

ODrive学习——添加485编码器支持

系列文章目录 文章目录 系列文章目录前言一、端口处理二、在Encoder中引入新的类型1.增加485类型2.增加串口的初始化操作3.数据处理 总结 前言 尝试在ODrive中添加485型的编码器的支持 一、端口处理 计划使用PA2及PA3作为485通信的端口。这样首先要把外部温度传感器的I/O口给…

JavaSE - 面向对象编程03

01 多态 01_01 认识多态 01_02 多态的好处和缺点 【1】好处:① 可以解耦合,扩展性更强,父类引用指向的子类对象可以随时切换,而后面的逻辑代码并不需要更改。 ② 使用父类引用可以作为方法的形参或返回类型来接收一切子类对象。…

CTFHub技能树-信息泄露-HG泄漏

目录 漏洞产生原因 解题过程 当开发人员使用 Mercurial 进行版本控制,对站点自动部署。如果配置不当,可能会将.hg 文件夹直接部署到线上环境。这就引起了 hg 泄露漏洞。 漏洞产生原因 Mercurial(hg)是一种分布式版本控制系统,它与Git类似也可以用于管…

微信小程序使用 ==== 粘性布局

目录 Chrome杀了个回马枪 position:sticky简介 你可能不知道的position:sticky 深入理解粘性定位的计算规则 粘性定位其他特征 代码实现 微信小程序在scroll-view中使用sticky Chrome杀了个回马枪 position:sticky早有耳闻也有所了解,后来,Chro…

Redis 配置

一、关系型数据库与非关系型数据库 1. 关系型数据库 关系型数据库是一种结构化数据库,基于关系模型(二维表格模型),适合记录数据。通过 SQL(结构化查询语言)进行数据的检索和操作。主流的关系型数据库包括…