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

news2024/11/19 13:27:41
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);
    }

    @Override
    public 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);
    }

    @Override
    public 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;
    }

    @Override
    public 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);
    }

    @Override
    public 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));
        }
    }

    @Override
    public void visit(Wine wine) {
        System.out.println("酒类: " + wine.getName() +",无折扣价格!");
        System.out.println("原价售卖: " +
                NumberFormat.getCurrencyInstance().format(wine.getPrice()));
    }

    @Override
    public 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);
	}
	//测试
	
	@Override
	public void accept(Visitor visitor) {
	//accept实现方法中调用访问者并将自己 "this" 传回。this是一个明确的身份,不存在任何泛型
	visitor.visit(this);
	}
}

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

4 访问者模式总结

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

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

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

相关文章

嵌入式软件工程师面试题——2025校招专题(四)

说明&#xff1a; 面试题来源于网络书籍&#xff0c;公司题目以及博主原创或修改&#xff08;题目大部分来源于各种公司&#xff09;&#xff1b;文中很多题目&#xff0c;或许大家直接编译器写完&#xff0c;1分钟就出结果了。但在这里博主希望每一个题目&#xff0c;大家都要…

python——requests模块

requests不是python的内置库&#xff0c;需要手动安装&#xff1a; pip install requests 一. 一个类型和六个属性 1.1 类型 requests访问url后返回的对象类型为requests.models.Response类型。 1.2 属性 下面是requests.models.Response类型对象的方法。 text&#xff1a;以…

【Javascript】json

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

OSG开发笔记(二十九):OSG加载模型文件、加载3DMax三维型文件Demo

​ 若该文为原创文章&#xff0c;未经允许不得转载 本文章博客地址&#xff1a;https://blog.csdn.net/qq21497936/article/details/134064988 各位读者&#xff0c;知识无穷而人力有穷&#xff0c;要么改需求&#xff0c;要么找专业人士&#xff0c;要么自己研究 红胖子(红模…

班主任必get,超实用的成绩发布方式

分享给老师们一个超级实用的教程&#xff0c;看看如何使用各种代码和Excel实现学生自主查询成绩的功能吧&#xff01;不用再繁琐的手动操作&#xff0c;让学生和家长自己就能查到成绩&#xff01; 成绩查询系统是什么&#xff1f; 成绩查询系统是一种方便学生查询考试成绩的应…

Android 和 iOS APP 测试的那些区别

目前市面上主流的移动操作系统就是 Android 和 iOS 两种&#xff0c;移动端测试本身就跟 Web 应用测试有自己的专项测试&#xff0c;比如安装、卸载、升级、消息推送、网络类型测试、弱网测试、中断测试、兼容性测试等都是区别于 Web 应用需要关注的测试领域。 那么&#xff0…

办公用品经营配送小程序商城的作用是什么

对办公人员来说&#xff0c;办公设备是不可缺少的&#xff0c;对办公用品经营商家来说&#xff0c;市场生意很高很多&#xff0c;但想要获取却也不容易&#xff0c;线下方式难以拓展&#xff0c;线上无平台&#xff0c;入驻第三方也有诸多限制与不足&#xff0c;私域是商家们增…

关键词搜索1688商品数据接口(标题|主图|SKU|价格|优惠价|掌柜昵称|店铺链接|店铺所在地)

1688商品列表接口是一个用于获取1688网站上商品列表信息的接口。通过该接口&#xff0c;您可以获取到1688网站上不同类别的商品列表&#xff0c;包括商品的名称、价格、图片等信息。 要使用1688商品列表接口&#xff0c;您需要按照以下步骤进行操作&#xff1a; 登录1688网站…

Linux 磁盘挂载2(文件系统格式化、磁盘挂载、VFS虚拟化文件系统)

目录 Linux文件系统 文件系统类型 Linux如何保存文件 VFS虚拟文件系统 文件格式化命令 mkfs 格式化文件系统 磁盘挂载命令 mount 临时挂载命令 umount 卸载文件系统 vim /etc/fstab 永久挂载 Linux文件系统 Linux 磁盘挂载1&#xff08;硬盘分区&#xff09;_linux磁…

高效遮挡!一键隐藏Logo标志,让您的内容更自由!

亲爱的用户&#xff0c;您是否曾经因为在营销、宣传、推广等领域使用的图片或视频中&#xff0c;存在不合适的Logo标志而感到烦恼&#xff1f;现在&#xff0c;我们向您推荐一款高效的遮挡工具&#xff0c;让您轻松隐藏Logo标志&#xff0c;让您的内容更自由&#xff01; 第一…

面试题:为什么HashMap 使用的时候指定容量?

文章目录 前言正文为什么要指定容量&#xff1f; 前言 其实可以看到我写了这么久的博客&#xff0c;很少去写hashMap的东西。 为什么&#xff1f;因为这个东西感觉是java面试必备的&#xff0c;我感觉大家都看到腻了&#xff0c;所以一直没怎么去写hashMap相关的。 本篇内容&…

eclispe项目中静态文件出现错误解决方法

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 很多时间…

C++项目——云备份-⑧-客户端各模块实现

文章目录 专栏导读1.客户端数据管理模块实现2.客户端文件检测模块实现3.客户端文件备份模块设计4.客户端文件备份模块实现 专栏导读 &#x1f338;作者简介&#xff1a;花想云 &#xff0c;在读本科生一枚&#xff0c;C/C领域新星创作者&#xff0c;新星计划导师&#xff0c;阿…

创建一个具有背景轮播和3D卡片翻转效果的个人名片网页

目录 项目展示 图片展示 前言 项目目标 项目目标 步骤 3&#xff1a;CSS 样式 步骤 4&#xff1a;JavaScript 动画 项目源码 知识点介绍 &#xff08;大佬请绕道&#xff09; HTML 结构的构建 2. CSS 样式的设计 3. JavaScript 动画的实现 4. 背景图轮播的逻辑 5…

java智慧工地云平台源码 人工智能AI+多系统集成+智能预警平台

智慧工地云平台源码 人工智能AI多系统集成智能预警平台 智慧工地企业级监管平台融入AIoT、移动互联网和物联网等领先技术&#xff0c;再结合工地“人、机、料、法、环”五大要素&#xff0c;劳务实名制管理、环境监测管理、安全施工管理、质量及能耗管理等智慧化应用&#xff0…

21.8 Python 使用BeautifulSoup库

BeautifulSoup库用于从HTML或XML文件中提取数据。它可以自动将复杂的HTML文档转换为树形结构&#xff0c;并提供简单的方法来搜索文档中的节点&#xff0c;使得我们可以轻松地遍历和修改HTML文档的内容。广泛用于Web爬虫和数据抽取应用程序中。 读者如果需要使用这个库&#x…

Leetcode---368周赛

题目列表 2908. 元素和最小的山形三元组 I 2909. 元素和最小的山形三元组 II 2910. 合法分组的最少组数 2911. 得到 K 个半回文串的最少修改次数 一、元素和最小的山形三元组I 没什么好说的&#xff0c;不会其他方法就直接暴力&#xff0c;时间复杂度O(n^3)&#xff0c;代…

SpringCloudGateway 入门

目录 POM 依赖一、内容网关的作用Spring-Cloud-Gateway的核心概念 二、基于Ribbon的负载均衡三、核心概念详细3.1 断言 Predicate3.2 过滤器3.2.1 内置过滤器3.2.2 自定义过滤器构造器&#xff08;原理&#xff09;资源结构Route / Predicate 的构造器构造器的增强器整体协同关…

人大金仓(Kingbase)部署

点击上方蓝字关注我 1. 介质下载 下载地址&#xff1a;https://www.kingbase.com.cn/rjcxxz/index.htm 选择安装包及授权文件&#xff1a;根据对应的操作系统类型选择安装包 2. 部署环境配置 2.1 部署环境&#xff1a; 8C 16G KylinV10SP3系统 2.2 修改操作系统内核参数 sy…

什么是pmp证书,pmp证书有什么用,pmp项目管理证书的认证考试时间是什么时候啊?

PMP是项目管理证书&#xff0c;目标是项目经理。 英文全称是Project Management Professional&#xff0c;中文全称叫做项目管理专业人士资格认证。 它是由美国项目管理协会&#xff08;PMI&#xff09;在全球范围内推出的针对项目经理的资格认证体系&#xff0c;严格评估项目…