24 行为型模式-访问者模式

1 访问者模式介绍

访问者模式在实际开发中使用的非常少,因为它比较难以实现并且应用该模式肯能会导致代码的可读性变差,可维护性变差,在没有特别必要的情况下,不建议使用访问者模式。
在这里插入图片描述

2 访问者模式原理

在这里插入图片描述
在这里插入图片描述

3 访问者模式实现

我们以超市购物为例,假设超市中的三类商品: 水果,糖果,酒水进行售卖. 我们可以忽略每种商品的计价方法,因为最终结账时由收银员统一集中处理,在商品类中添加计价方法是不合理的设计.我们先来定义糖果类和酒类、水果类.

/*** 抽象商品父类**/
public abstract class Product {private String name; //商品名private LocalDate produceDate; //生产日期private double price; //商品价格public Product(String name, LocalDate produceDate, double price) {this.name = name;this.produceDate = produceDate;this.price = price;}public String getName() {return name;}public void setName(String name) {this.name = name;}public LocalDate getProduceDate() {return produceDate;}public void setProduceDate(LocalDate produceDate) {this.produceDate = produceDate;}public double getPrice() {return price;}public void setPrice(double price) {this.price = price;}
}
/*** 糖果类**/
public class Candy extends Product implements Acceptable{public Candy(String name, LocalDate produceDate, double price) {super(name, produceDate, price);}@Overridepublic void accept(Visitor visitor) {//在accept方法中调用访问者, 并将自己 this 传递回去.visitor.visit(this);}
}
/*** 酒水类**/
public class Wine extends Product implements Acceptable{public Wine(String name, LocalDate produceDate, double price) {super(name, produceDate, price);}@Overridepublic void accept(Visitor visitor) {visitor.visit(this);}
}
/*** 水果类**/
public class Fruit extends Product implements Acceptable{private double weight;  //重量public Fruit(String name, LocalDate produceDate, double price, double weight) {super(name, produceDate, price);this.weight = weight;}public double getWeight() {return weight;}public void setWeight(double weight) {this.weight = weight;}@Overridepublic void accept(Visitor visitor) {visitor.visit(this);}
}
访问者接口

收银员就类似于访问者,访问用户选择的商品,我们假设根据生产日期进行打折,过期商品不能够出售. 注意这种计价策略不适用于酒类,作为收银员要对不同商品应用不同的计价方法。

/*** 访问者接口 - 根据入参的不同调用对应的重载方法**/
public interface Visitor {public void visit(Candy candy); //糖果重载方法public void visit(Wine wine); //酒类重载方法public void visit(Fruit fruit); //水果重载方法
}
具体访问者

创建计价业务类,对三类商品进行折扣计价,折扣计价访问者的三个重载方法分别实现了3类商品的计价方法,体现了visit() 方法的多态性.

/*** 折扣计价访问者类**/
public class DiscountVisitor implements Visitor {private LocalDate billDate;public DiscountVisitor(LocalDate billDate) {this.billDate = billDate;System.out.println("结算日期: " + billDate);}@Overridepublic void visit(Candy candy) {System.out.println("糖果: " + candy.getName());//糖果大于180天,禁止售卖,否则糖果一律九折long days = billDate.toEpochDay() - candy.getProduceDate().toEpochDay();if(days > 180){System.out.println("超过半年的糖果,请勿食用!");}else{double realPrice = candy.getPrice() * 0.9;System.out.println("糖果打折后的价格为: " +NumberFormat.getCurrencyInstance().format(realPrice));}}@Overridepublic void visit(Wine wine) {System.out.println("酒类: " + wine.getName() +",无折扣价格!");System.out.println("原价售卖: " +NumberFormat.getCurrencyInstance().format(wine.getPrice()));}@Overridepublic void visit(Fruit fruit) {System.out.println("水果: " + fruit.getName());long days = billDate.toEpochDay() - fruit.getProduceDate().toEpochDay();double rate = 0;if(days > 7){System.out.println("超过七天的水果,请勿食用!");}else if(days > 3){rate = 0.5;}else{rate = 1;}double realPrice = fruit.getPrice() * fruit.getWeight() * rate;System.out.println("水果价格为: " +NumberFormat.getCurrencyInstance().format(realPrice));}
}
public class Client {public static void main(String[] args) {//        Candy candy = new Candy("德芙巧克力", LocalDate.of(2022, 1, 1), 10.0);
//
//        Visitor visitor = new DiscountVisitor(LocalDate.of(2022,10,5));
//        visitor.visit(candy);//将3件商品加入购物车
//        List<Product> products = Arrays.asList(
//                new Candy("金丝猴奶糖",LocalDate.of(2022,10,1),10),
//                new Wine("郎酒",LocalDate.of(2022,10,1),1000),
//                new Fruit("草莓",LocalDate.of(2022,10,8),50,1)
//                );//        Visitor visitor = new DiscountVisitor(LocalDate.of(2022,10,5));
//        for (Product product : products) {
//            //visitor.visit();
//        }//模拟添加多个商品List<Acceptable> list = Arrays.asList(new Candy("金丝猴奶糖",LocalDate.of(2022,10,1),10),new Wine("郎酒",LocalDate.of(2022,10,1),1000),new Fruit("草莓",LocalDate.of(2022,10,8),50,1));Visitor visitor = new DiscountVisitor(LocalDate.of(2022,10,11));for (Acceptable product : list) {product.accept(visitor);}}
}

上面的代码虽然可以完成当前的需求,但是设想一下这样一个场景: 由于访问者的重载方法只能对当个的具体商品进行计价,如果顾客选择了多件商品来结账时,就可能会引起重载方法的派发问题(到底该由谁来计算的问题).

首先我们定义一个接待访问者的类 Acceptable,其中定义了一个accept(Visitor visitor)方法, 只要是visitor的子类都可以接收.

/*** 接待者这接口 (抽象元素角色)**/
public interface Acceptable {//接收所有的Visitor访问者的子类public void accept(Visitor visitor);
}
/**
* 糖果类
* @author spikeCong
* @date 2022/10/18
**/
public class Candy extends Product implements Acceptable{public Candy(String name, LocalDate producedDate,double price) {super(name, producedDate, price);}//测试@Overridepublic void accept(Visitor visitor) {//accept实现方法中调用访问者并将自己 "this" 传回。this是一个明确的身份,不存在任何泛型visitor.visit(this);}
}

代码编写到此出,就可以应对计价方式或者业务逻辑的变化了,访问者模式成功地将数据资源(需实现接待者接口)与数据算法 (需实现访问者接口)分离开来。重载方法的使用让多样化的算法自成体系,多态化的访问者接口保证了系
统算法的可扩展性,数据则保持相对固定,最终形成⼀个算法类对应⼀套数据。

4 访问者模式总结

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

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

相关文章

JVM(二)

一,运行时数据区 Java虚拟机在运行Java程序过程中管理的内存区域,称之为运行时数据区。 1.1 程序计数器 程序计数器(Program Counter Register)也叫PC寄存器,每个线程会通过程序计数器记录当前要执行的的字节码指令的地址。 在加载阶段,虚拟机将字节码文件中的指令读取…

2024北京智慧养老展/北京老年科技产品展/北京养老产业展会

以展为媒&#xff0c;对话世界&#xff0c;2024第11届中国&#xff08;北京&#xff09;国际智慧养老展览会4月10日盛大启幕 2024第11届中国&#xff08;北京&#xff09;国际智慧养老产业展览会 The 2024 China (Beijing) international pension Industry Exhibition 时间&a…

多线程面试相关知识点

文章目录 (一) 进程线程和协程的区别创建线程的4种方式1. 继承Thread类2. 实现runnable接口3. 实现Callable接口4. 线程池创建 runnable 和 callable 有什么区别线程的 run()和 start()有什么区别&#xff1f;线程之间的状态变化notify()和 notifyAll()有什么区别&#xff1f;j…

HPV感染的风险:闫会宁主任分析酒店环境中的常见因素

人类乳头瘤病毒(HPV)是一种普遍存在的病毒&#xff0c;其存在和传播方式多种多样。近年来&#xff0c;人们对于HPV的认识不断深入&#xff0c;知道其在酒店环境中的传播风险。本文将探讨哪些情况下在酒店可能感染HPV。 一、HPV的传播方式 HPV主要通过直接接触传播&#xff0c…

Day 46 动态规划 part12

Day 46 动态规划 part12 解题理解309714 2道题目 309. 买卖股票的最佳时机含冷冻期 714. 买卖股票的最佳时机含手续费 解题理解 309 这道题不太好理解&#xff0c;需要考虑的情况很多并且不好确定。可以设置每天的状态有4种&#xff1a; dp[i][0] 今天持有股票 dp[i][1] 今天…

小结笔记:多位管理大师关于管理的要素的论述

最近在看《刘澜管理学》&#xff0c;其中有提到多位管理大师关于管理的要素的论述,笔记如下&#xff1a; 法约尔的管理五要素 这就是在前言中提到过的法约尔的管理五要素模型。 第一个“管理”学者 法约尔可以说是第一个专门的“管理”学者。在法约尔之前&#xff0c;没有人专门…

数据湖Iceberg介绍和使用(集成Hive、SparkSQL、FlinkSQL)

文章目录 简介概述作用特性数据存储、计算引擎插件化实时流批一体数据表演化&#xff08;Table Evolution&#xff09;模式演化&#xff08;Schema Evolution&#xff09;分区演化&#xff08;Partition Evolution&#xff09;列顺序演化&#xff08;Sort Order Evolution&…

python:使用Scikit-image对遥感影像进行梯度特征提取(gradient)

作者:CSDN @ _养乐多_ 在本博客中,我们将介绍如何使用Scikit-Image来进行梯度特征提取(gradient),并且提供一个示例代码,演示了如何在单波段遥感图像上应用这些方法。 梯度特征是指用于表示图像中亮度或颜色变化的特征。它包括两个关键成分:梯度幅值和梯度方向。梯度幅…

RabbitMQ (4)

RabbitMQ (4) 文章目录 1. 死信的概念2. 死信的来源3. 死信代码案例3.1 TTL 过期时间3.2 超过队列最大长度3.3 拒绝消息 前言   上文我们已经学习完 交换机 &#xff0c;知道了几个交换机的使用 &#xff0c;下面我们来学习一下 死信队列 1. 死信的概念 先从概念解释上搞清楚这…

Redis 与 MySQL 一致性 实现方案

正常情况下的流程是&#xff1a;请求来了&#xff0c;先检查 Redis 有没有数据&#xff0c;有返回&#xff1b;没有便查询 MySQL 然后 放入 Redis。 此时&#xff0c;如果 MySQL 的数据发生了变化&#xff0c;所以需要同步到 Redis 中。 解决方法&#xff1a;MySQL 中的数据更新…

【C++】mapset的底层结构 -- AVL树(高度平衡二叉搜索树)

前面我们对 map / multimap / set / multiset 进行了简单的介绍&#xff0c;可以发现&#xff0c;这几个容器有个共同点是&#xff1a;其底层都是按照二叉搜索树来实现的。 但是二叉搜索树有其自身的缺陷&#xff0c;假如往树中插入的元素有序或者接近有序&#xff0c;二叉搜索…

漏洞复现-phpmyadmin_SQL注入 (CVE-2020-5504)

phpmyadmin SQL注入 _&#xff08;CVE-2020-5504&#xff09; 漏洞信息 CVE-2020-5504sql注入漏洞Phpmyadmin 5.00以下 描述 ​ phpMyAdmin是Phpmyadmin团队的一套免费的、基于Web的MySQL数据库管理工具。该工具能够创建和删除数据库&#xff0c;创建、删除、修改数据库表&…

Java学习 5.习题2.

练习题1&#xff1a;判断一个数字是偶数还是奇数 int num110;if(num1%20) {System.out.println("num1是一个偶数");}else{System.out.println("num1是一个奇数");} 练习题2&#xff1a;判断一个数是正数还是负数还是0 int num2-5;if(num2>0) {System.ou…

vantUI(Tabbar标签页)浏览器返回上一页的失效问题

在开发中遇到这样一个问题&#xff0c;由页面1切换到页面2&#xff0c;再点击浏览器的回退&#xff0c;无法回退到页面1。 开始以为是路由配置的有问题&#xff0c;但是子页面可以正常回退&#xff0c;因为replace只是替换路由&#xff0c;而不会往history栈中记录路由&#x…

yarn install 这个命令安装如何加速

yarn install 命令用来安装项目依赖&#xff0c;其速度受多种因素影响&#xff0c;如网络速度、npm/yarn包的源服务器、以及本地缓存等。以下是一些可能帮助你加速 yarn install 的方法&#xff1a; 1. 使用国内镜像 如果你在中国&#xff0c;可以使用淘宝的 npm 镜像&#x…

Android 13.0 系统多个播放器app时,设置默认播放器

1.概述 在13.0的系统产品开发中,对于在系统中有多个播放器的时候,这时候如果调用代码打开播放器,会出现多个播放器列表让用户 选择启动哪个播放器,所以产品开发需求需要设置默认播放器,当打开播放器的时候,就直接打开播放器就可以了,所以就需要 了解查询播放器列表流程,…

【Javascript】json

目录 什么是json&#xff1f; 书写格式 json 序列化和反序列化 序列化 反序列化 什么是json&#xff1f; JSON(JavaScript Object Notation)是⼀种轻量级的数据交换格式&#xff0c;它基于JavaScript的⼀个⼦集&#xff0c;易于⼈的编写和阅读&#xff0c;也易于机器解析…

栈、队列、矩阵的总结

栈的应用 括号匹配 表达式求值&#xff08;中缀&#xff0c;后缀&#xff09; 中缀转后缀&#xff08;机算&#xff09; 中缀机算 后缀机算 总结 特殊矩阵 对称矩阵的压缩存储 三角矩阵 三对角矩阵 稀疏矩阵的压缩存储

OpenCV官方教程中文版 —— 傅里叶变换

OpenCV官方教程中文版 —— 傅里叶变换 前言一、原理二、Numpy 中的傅里叶变换三、OpenCV 中的傅里叶变换四、为什么拉普拉斯算子是高通滤波器&#xff1f; 前言 本小节我们将要学习&#xff1a; • 使用 OpenCV 对图像进行傅里叶变换 • 使用 Numpy 中 FFT&#xff08;快速…

多线程线程池

线程安全问题 当多个线程同时操作同一个共享资源的时候&#xff0c;可能会出现结果不符合预期的问题 解决安全问题 方式一: 同步代码块 作用: 把访问共享资源的核心代码给上锁&#xff0c;以此保证线程安全 格式: synchronized(同步锁) { 访问共享资源的核心代…