掌握设计模式--访问者模式

news/2025/3/29 5:17:04/文章来源:https://www.cnblogs.com/dennyLee2025/p/18792711

访问者模式(Visitor Pattern)

访问者模式(Visitor Pattern)是一种行为设计模式,它允许你将操作(方法)封装到另一个类中,使得你可以在不修改现有类的情况下,向其添加新的操作。

核心思想是将数据结构和对数据的操作分离通过访问者对象来对数据进行操作,而不是将操作方法直接嵌入数据结构本身。

组成部分

  1. Visitor(访问者):这是一个接口或抽象类,定义了针对各个元素类(通常是数据结构中的元素)的操作方法。

  2. ConcreteVisitor(具体访问者):实现访问者接口的具体类,定义了具体的操作。

  3. Element(元素):这是一个接口或抽象类,定义了接受访问者的接口。通常是数据结构的元素。

  4. ConcreteElement(具体元素):实现元素接口的具体类,定义数据结构的具体元素。

  5. ObjectStructure(对象结构):维护一个元素集合,通常是一个集合或树形结构,可以接受访问者的访问。

案例代码

假设我们有一个简单的元素结构,表示不同类型的账单项目,我们要为每种账单项目添加不同的计算方式,比如食物和服装账单,分别使用不同的折扣,这个折扣会经常发生变化。可以通过访问者模式来解决这个问题。

案例类图

image

账单--元素接口

public interface BillItem {void accept(BillVisitor visitor);
}

账单--具体元素类:食物类和服装类

public class FoodItem implements BillItem {private double price;public FoodItem(double price) {this.price = price;}public double getPrice() {return price;}@Overridepublic void accept(BillVisitor visitor) {visitor.visit(this);}
}// 具体元素:ClothingItem
class ClothingItem implements BillItem {private double price;public ClothingItem(double price) {this.price = price;}public double getPrice() {return price;}@Overridepublic void accept(BillVisitor visitor) {visitor.visit(this);}
}

账单--访问者接口

public interface BillVisitor {void visit(FoodItem foodItem);void visit(ClothingItem clothingItem);
}

账单--具体访问者

public class DiscountVisitor implements BillVisitor {@Overridepublic void visit(FoodItem foodItem) {// 9折double discountedPrice = foodItem.getPrice() * 0.9;System.out.println("食品折扣价格: " + discountedPrice);}@Overridepublic void visit(ClothingItem clothingItem) {// 8折double discountedPrice = clothingItem.getPrice() * 0.8;System.out.println("服装项目的折扣价格: " + discountedPrice);}
}

测试客户端

public class VisitorPatternExample {public static void main(String[] args) {BillItem food = new FoodItem(100.0);BillItem clothing = new ClothingItem(200.0);BillVisitor discountVisitor = new DiscountVisitor();food.accept(discountVisitor);clothing.accept(discountVisitor);}
}

测试输出结果

食品折扣价格: 90.0

服装项目的折扣价格: 160.0

解释

  1. BillVisitor 是访问者接口,它定义了针对不同类型账单项目(如 FoodItemClothingItem)的操作方法。
  2. BillItem 是元素接口,所有账单项目类都实现该接口。
  3. FoodItemClothingItem 是具体的元素类,它们实现了 BillItem 接口,并在 accept 方法中调用访问者的相应操作。
  4. DiscountVisitor 是一个具体的访问者,计算并打印带折扣的价格。

当需要为账单项目添加新的操作时,只需要添加一个新的访问者类,而不必修改现有的 FoodItemClothingItem 类。这样就实现了操作和数据结构的分离。

优缺点和适用场景

优点

  • 扩展性好:可以方便地添加新的操作而不修改现有元素类(数据结构)。
  • 将数据结构和操作解耦:操作逻辑集中在访问者类中,数据结构集中在元素类中。
  • 符合开闭原则:可以在不修改原有代码的基础上,增加新的功能。

缺点

  • 元素类难以修改:如果元素类的结构改变,需要修改所有访问者类,这在某些情况下会变得麻烦。
  • 不适合频繁变动的数据结构:访问者模式适用于元素类结构较为稳定的情况,如果元素类需要频繁变化,访问者模式可能导致很多不必要的修改。

适用场景

  • 元素类结构稳定,但你需要在不修改这些类的情况下,增加新的操作。
  • 需要对一个对象结构中的元素进行不同的操作时,访问者模式可以避免在元素类中写入大量的业务逻辑。

访问者模式是当你有一个固定的元素结构,但需要频繁进行不同的操作时非常有用的设计模式。

总结

访问者模式是一种行为设计模式,其核心在于通过将操作逻辑封装到访问者对象中,使得在不修改对象结构的前提下,能够为一组对象添加新的行为,同时将数据结构行为操作解耦,适用于复杂对象结构中操作变化频繁的场景。

image

需要查看往期设计模式文章的,可以在个人主页中或者文章开头的集合中查看,可关注我,持续更新中。。。


超实用的SpringAOP实战之日志记录

2023年下半年软考考试重磅消息

通过软考后却领取不到实体证书?

计算机算法设计与分析(第5版)

Java全栈学习路线、学习资源和面试题一条龙

软考证书=职称证书?

软考中级--软件设计师毫无保留的备考分享

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

双向广搜-BiDirectional BFS

双向广搜 文章目录 前言前言 复习acwing算法提高课的内容,本篇为讲解算法:双向广搜 一、双向广搜 双向广搜其实就是两个bfs,我们知道bfs是一种暴力的做题方法,搜索树长下图所示:我们会发现搜索树越来越宽,每一层的搜索量增加,如果数据范围很大的话,显然是会TLE的,那么…

读DAMA数据管理知识体系指南31参考数据和主数据概念(上)

读DAMA数据管理知识体系指南31参考数据和主数据概念(上)1. 业务驱动因素 1.1. 满足组织数据需求1.1.1. 组织中的多个业务领域需要访问相同的数据集,并且他们都相信这些数据集是完整的、最新的、一致的1.2. 管理数据质量1.2.1. 数据的不一致、质量问题和差异均会导致决策错误…

生成式 AI 和 LLM 简介 起源 历史记录

领域 年份 定义人工智能 (AI) 1956 计算机科学领域,旨在创造能够复制或超越人类智能的智能机器。机器学习 (Machine Learning) 1997 人工智能的子集,使机器能够从现有数据中学习并根据这些数据进行决策或预测。深度学习 (Deep Learning) 2012 一种机器学习技术,通过使用多层…

拿到代理对象,如何调用增强方法

步骤1 前面已经创建了MathCal的代理对象了,我们在调用方法时加一个断点这里返回的确实是代理对象,这个对象中保存了详细信息(增强器,原始对象等),我们进入bean.add(2, 10) 中,来到 org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor.intercept(…

如何保证消息队列的消息只能被消费一次

如何保证消息队列的消息只能被消费一次,首先先保证消息不会丢失 首先先生产者到消费者到消费者有哪些场景会消息丢失一、问题场景 场景一、生产者发送到消息队列失败 场景二、消息队列接受到消息磁盘化失败 场景三、消费者接受到消息消费失败 二、场景原因,如何解决 1、场景一…

Day22_java方法

Java方法 方法重载 package com.xiang.method;public class Demo02 {public static void main(String[] args) {int max = max(20, 100, 10);System.out.println(max);}// 比大小public static int max(int num1,int num2){int result = 0;if (num1 == num2){System.out.printl…

文献阅读《Spectral Networks and Deep Locally Connected Networks on Graphs》

参考博客 第一代图卷积网络:图的频域网络与深度局部连接网络 - 知乎 (zhihu.com) 论文解读一代GCN《Spectral Networks and Locally Connected Networks on Graphs》 - 别关注我了,私信我吧 - 博客园 (cnblogs.com) 论文核心 卷积神经网络得益于所处理的数据具有局部平移不变…

【CodeForces训练记录】Codeforces Round 1013 (Div. 3)

训练情况赛后反思 A题题目读半天,发现日期有前导零,div3还是比较基础一点,但是感觉自己还是不够熟练,D题看出来二分但是调了挺久的 A题 判断取多少个数之后才能构成 20250301,我们维护数字的出现次数,直到所有数字的出现次数全部大于等于 20250301 的出现次数时输出位置即…

字符串问题的江湖奇宝:进制哈希

江湖中,剑客以快制胜,而算法竞赛里,字符串哈希(String Hashing)便是那柄出招如电的快剑。 各种字符串问题纷乱复杂,各种字符串算法招式繁复,需苦练内功心法。但字符串哈希算法却只凭一招:将字符串化作数字,以数论为刃,至简之道斩尽来犯之敌。 但此招并非无懈可击。若…

HW-1

1.选项A是正确的,它表示的是极小项m6的正确形式。极小项m6对应的是变量a=0,b=1,c=1,d=0的情况,因此其表达式应为(\overline{a} \cdot b \cdot c \cdot \overline{d}),即选项A。 其他选项的分析:选项B是一个或项,不符合极小项的定义。 选项C缺少变量a和d,不是一个完整的…

为什么nn.Linear 的weight 是 (out_features, in_features)

在PyTorch的nn.Linear中,权重矩阵的形状为(out_features, in_features)。这是因为线性变换的实现方式为:具体来说:当创建nn.Linear(10, 60)时,in_features=10,out_features=60,因此权重的形状是(60, 10)。 输入张量t的形状为(2, 5, 10),与转置后的权重a.weight.T(形状(…