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.rhkb.cn/news/174811.html

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

相关文章

.NET、VUE利用RSA加密完成登录并且发放JWT令牌设置权限访问

后端生成公钥私钥 使用RSA.ToXmlString(Boolean) 方法生成公钥以及私钥。 RSACryptoServiceProvider rSA new(); string pubKey rSA.ToXmlString(false);//公钥 string priKey rSA.ToXmlString(true);//私钥 后端将生成的公钥发送给前端 创建一个get请求&#xff0c;将…

【Linux】centOS7安装配置及Linux的常用命令---超详细

一&#xff0c;centOS 1.1 centOS的概念 CentOS&#xff08;Community Enterprise Operating System&#xff09;是一个由社区支持的企业级操作系统&#xff0c;它是以Red Hat Enterprise Linux&#xff08;RHEL&#xff09;源代码为基础构建的。CentOS提供了一个稳定、可靠且…

macOS M1安装wxPython报错‘tiff.h‘ file not found的解决方法

macOS12.6.6 M1安装wxPython失败&#xff1a; 报错如下&#xff1a; imagtiff.cpp:37:14: fatal error: tiff.h file not found解决办法&#xff1a; 下载源文件重新编译&#xff08;很快&#xff0c;5分钟全部搞定&#xff09;&#xff0c;分三步走&#xff1a; 第一步&…

完整的电商平台后端API开发总结

对于开发一个Web项目来说&#xff0c;无论是电商还是其他品类的项目&#xff0c;注册与登录模块都是必不可少的&#xff1b;注册登录功能也是我们在日常生活中最长接触的&#xff0c;对于这个业务场景的需求与逻辑大概是没有什么需要详细介绍的&#xff0c;市面上常见的邮箱注册…

如何将word格式的文档转换成markdown格式的文档

如何将word格式的文档转换成markdown格式的文档 前言 A. 介绍Markdown和Word格式文档 什么是Markdown&#xff1f; Markdown是一种轻量级标记语言&#xff0c;旨在简化文本格式化和排版的过程。它以纯文本形式编写&#xff0c;通过使用简单的标记语法&#xff0c;使文档更具…

C代码的单元测试

C代码中集成gtest单元测试_gtest测试c语言_山河故人~的博客-CSDN博客 Linux安装gtest_gtest安装_山河故人~的博客-CSDN博客 一&#xff1a;安装gtest: 1. 安装gtest 采用源码安装的方式&#xff0c;需确保cmake已经安装。 git clone https://github.com/google/googletest …

IPv6地址配置方式

IPv6地址分类 IPv6地址分为单播地址、任播地址&#xff08;Anycast Address&#xff09;、组播地址三种类型。和IPv4相比&#xff0c;取消了广播地址类型&#xff0c;以更丰富的组播地址代替&#xff0c;同时增加了任播地址类型。 单播地址 IPv6单播地址标识了一个接口&…

如何创建 Spring Boot 项目

如果有pom.xml有插件异常&#xff0c;可以先删除。 maven配置要配置好 然后yaml&#xff0c;再启动就行 server:port: 9991 spring:application:name: demo3参考 如何创建 Spring Boot 项目_创建springboot项目_良月初十♧的博客-CSDN博客

【多线程面试题十六】、谈谈ReentrantLock的实现原理

文章底部有个人公众号&#xff1a;热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享&#xff1f; 踩过的坑没必要让别人在再踩&#xff0c;自己复盘也能加深记忆。利己利人、所谓双赢。 面试官&#xff1a;谈谈ReentrantLock的实现…

Android NDK开发详解之CMake工具链

Android NDK开发详解之CMake工具链 CMake 工具链文件用法工具链参数ANDROID_ABIANDROID_ARM_MODEANDROID_ARM_NEONANDROID_LDANDROID_NATIVE_API_LEVELANDROID_PLATFORMANDROID_STL 了解 CMake 构建命令使用预构建库构建第三方代码CMake 中的 YASM 支持报告问题 Android NDK 支…

本地生活商家用批量剪辑有用吗?

可以用&#xff0c;现在很多本地生活商家&#xff0c;都会通过借助批量剪辑工具来提升视频的曝光量&#xff0c;从而带动店铺的客流量。 推荐本地生活商家使用超级编导批量剪辑工具&#xff0c;这是一款0基础小白也可以很快上手的批量剪辑工具&#xff0c;剪辑页面布局以及功能…

c: two-dimensional array

/*** ****************************************************************************** file twoDimensional.h* brief 二维数组 Pointers and 2-D arrays* author geovindu,Geovin Du,涂聚文 (geovindu163.com)* ide: vscode c11,c17 windows 10* date…

VPS是什么?详解亚马逊云科技Amazon Lightsail(VPS)虚拟专用服务器

2006年&#xff0c;南非开普敦&#xff0c;亚马逊推出了WBS&#xff0c;以网络服务的形式向企业提供基础的IT服务。亚马逊云科技的一小步&#xff0c;在无数技术更迭&#xff0c;天才设计师和程序员的努力与基础设施建设的完善之下成为了人类科技进展的一大步。 亚马逊云科技可…

电路的电线的拼接

不积跬步无以至千里&#xff0c;今天小编也是复习今天学习的内容&#xff0c;废话不多说&#xff0c;看博客吧&#xff01;&#xff01;&#xff01; 目录 准备条件 操作 成品 准备条件 操作 将定制的套管插入导线当中&#xff0c;24V或者0V是尖端的端子&#xff0c;后面根…

web前端常见开发工具汇总 你用过几个?

搬运旗下公众号的内容~ 目录 1.记事本 2.Visual studio code 3.Hbuilder 4.Eclipse 5.Webstorm 6.Notepad 随着信息时代的不断进步&#xff0c;互联网在人类社会中所占的地位愈发举足轻重。大大小小的网站&#xff0c;构成了如今光怪陆离的网络社会。我们知道&#xff0c…

Sprint Cloud Stream整合RocketMq和websocket实现消息发布订阅

1.引入RocketMQ依赖&#xff1a;首先&#xff0c;在pom.xml文件中添加RocketMQ的依赖&#xff1a; <dependency><groupId>org.apache.rocketmq</groupId><artifactId>rocketmq-spring-boot-starter</artifactId><version>2.2.0</versi…

轻量级 IDE 文本编辑器 Geany 发布 2.0

Geany 是功能强大、稳定、轻量的开发者专用文本编辑器&#xff0c;支持 Linux、Windows 和 macOS&#xff0c;内置支持 50 多种编程语言。 2005 年Geany 发布首个版本 0.1。上周四刚好是 Geany 诞生 18 周年纪念日&#xff0c;官方发布了 2.0 正式版以表庆祝。 下载地址&#…

正则表达式引擎比较(翻译自:A comparison of regex engines)

原文&#xff1a; A comparison of regex engines – Rust Leipzig 引言 正则表达式&#xff08;或简称regex&#xff09;通常用于模式搜索算法。 有许多不同的正则表达式引擎提供不同的表达式支持、性能约束和语言绑定。 基于 John Maddock 之前的工作 (regex comparison)和…

window11 更改 vscode 插件目录,释放C盘内存

由于经常使用vscode开发会安装一些代码提示插件&#xff0c;然后C盘内容会逐渐缩小&#xff0c;最终排查定位到vscode。这个吃内存不眨眼的家伙。 建议&#xff1a;不要把插件目录和vscode安装目录放在同一个位置&#xff0c;不然这样vscode更新后&#xff0c;插件也会消失。 v…

Git窗口打开vim后如何退出编辑(IDEA/Goland等编辑器)

最近在学习git高级操作过程中&#xff0c;遇到了一下问题&#xff1a; 我在学习Git合并多个commit为一个的时候&#xff0c;需要输入一个命令 git rebase -i HEAD~2 这说明已经是编辑模式了。当我写好后&#xff0c;我还按照原来在linux上的按下ESC键&#xff0c;但是只是光…