设计模式—访问者模式

 需求:店铺采购了一批水果(苹果及橘子),现在市场监督局来店里检查过期的水果。

public class Fruit {

    private String name;
    private Date pickDate;

    public Fruit(String name, Date pickDate) {
        this.name = name;
        this.pickDate = pickDate;
    }

    public String getName() {
        return name;
    }

    public Date getPickDate() {
        return pickDate;
    }

    @Override
    public String toString() {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm");
        return "{" +
                "name='" + name + '\'' +
                ", 采摘日期=" + simpleDateFormat.format(pickDate) +
                '}';
    }
}

public class Apple extends Fruit{

    public Apple(String name, Date pickDate) {
        super(name, pickDate);
    }
    
}

public class Orange extends Fruit{
    public Orange(String name, Date pickDate) {
        super(name, pickDate);
    }
}

public class FruitShop  {

    private final String name;

    public FruitShop(String name) {
        this.name = name;
    }

    private final List<Fruit> fruitList = new ArrayList<>();

    public List<Fruit> getFruitList() {
        return fruitList;
    }

    public String getName() {
        return name;
    }

}

public class FruitMarket {

    public static void main(String[] args) throws ParseException {
        FruitShop myShop = new FruitShop("老果农");
        purchaseFruit(myShop);
        expCheck("市场监督局",myShop); //市场监督局检查过期水果:苹果过期时间5天,橘子过期时间10天
        expCheck("城管",myShop); //城管检查过期水果:苹果过期时间3天,橘子过期时间6天
        newArrival(myShop);//新品上市水果
    }

    // 采购水果
    public static void purchaseFruit(FruitShop shop) throws ParseException {
        List<Fruit> fruitList = shop.getFruitList();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm");
        fruitList.add(new Apple("富士康",simpleDateFormat.parse("2023-06-21 12:00")));
        fruitList.add(new Orange("赣南脐橙", simpleDateFormat.parse("2023-05-25 18:00")));
        fruitList.add(new Orange("韶关帝王橘", simpleDateFormat.parse("2023-6-18 21:00")));
        fruitList.add(new Apple("王掌柜",simpleDateFormat.parse("2023-06-11 12:00")));
    }

    // 过期检查
    public static void expCheck(String development,FruitShop shop) {
        int appleExpDay = 0, orangeExpDay = 0;
        if ("市场监督局".equals(development)) {
            appleExpDay = 5;
            orangeExpDay = 10;
        } else if ("城管".equals(development)) {
            appleExpDay = 2;
            orangeExpDay = 4;
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        calendar.add(Calendar.DATE,-appleExpDay);
        Date appleExpDate = calendar.getTime();
        calendar.setTime(new Date());
        calendar.add(Calendar.DATE,-orangeExpDay);
        Date orangeExpDate = calendar.getTime();
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm");
        System.out.println(development + "检查 苹果过期天数:" + appleExpDay + "  橘子过期天数:" + orangeExpDay + "  检查时间:" + dateFormat.format(new Date()) + " " + shop.getName());
        for (Fruit fruit : shop.getFruitList()) {
            boolean exp = false;
            if (fruit instanceof Apple) {
                if (appleExpDate.after(fruit.getPickDate())) exp = true;
            } else if (fruit instanceof Orange) {
                if (orangeExpDate.after(fruit.getPickDate())) exp = true;
            }
            if (exp) System.out.println(fruit + "  过期! ");
        }
        System.out.println("-------");
    }
   
}

如果此时再添加一个操作:找出新品上市的水果。

// 新品上市 苹果为2023-06-20 后采摘, 橘子为2023-06-21后采摘
    public static void newArrival(FruitShop shop) throws ParseException {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        Date appleNewDate = simpleDateFormat.parse("2023-06-20");
        Date orangeNewDate = simpleDateFormat.parse("2023-06-21");
        for (Fruit fruit : shop.getFruitList()) {
            boolean isNew = false;
            if (fruit instanceof Apple) {
                if(appleNewDate.before(fruit.getPickDate())) isNew = true;
            } else if (fruit instanceof  Orange) {
                if(orangeNewDate.before(fruit.getPickDate())) isNew= true;
            }
            if (isNew) System.out.println(fruit.getName() + " 新品上市");
        }
    }

上述代码中,FruitMarket为应付新增操作,增加了相应的方法来满足要求,但这样破坏了FruitMarket的结构。

访问者模式

本质是将数据结构和数据操作分离,通过定义一个访问者对象,实现对数据结构中各个元素的访问和处理,从而达到解耦和灵活性的目的。

图 访问者UML

Visitor 抽象访问者

为ObjectStructure对象结构中的每一个Element都声明一个Visit操作。

ConcreteVisitor具体访问者

Visitor的实现,实现要真正被添加到对象结构中的功能。

ObjectStructure 对象结构

通常包含多个被访问的对象,可以是一个复合或是一个集合。

Element 抽象元素

为Visitor声明一个accept方法,实现元素与访问者的绑定。

ConcreteElement 具体元素

对象结构体中具体的对象,是被访问的对象。

/**
 * 水果元素抽象类
 */
public abstract class FruitElement {

    private String name;
    private Date pickDate;

    public FruitElement(String name, Date pickDate) {
        this.name = name;
        this.pickDate = pickDate;
    }

    public String getName() {
        return name;
    }

    public Date getPickDate() {
        return pickDate;
    }

    public abstract void accept(Visitor visitor);

    @Override
    public String toString() {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm");
        return "{" +
                "name='" + name + '\'' +
                ", 采摘日期=" + simpleDateFormat.format(pickDate) +
                '}';
    }

}
public class AppleElement extends FruitElement{

    public AppleElement(String name, Date pickDate) {
        super(name, pickDate);
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }

}

public class OrangeElement extends FruitElement{

    public OrangeElement(String name, Date pickDate) {
        super(name, pickDate);
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }

}
/**
 * 访问者
 */
public interface Visitor {

    void visit(AppleElement apple);

    void visit(OrangeElement orange);

}

/**
 * 政府检查部门
 */
public class DevelopmentVisitor implements Visitor{

    private final int appleExpDay;

    private final int orangeExpDay;

    public DevelopmentVisitor(String name, int appleExpDay, int orangeExpDay) {
        this.appleExpDay = appleExpDay;
        this.orangeExpDay = orangeExpDay;
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm");
        System.out.println(name + " 检查过期水果, 苹果过期天数:" + appleExpDay + ",橘子过期天数:" +
                orangeExpDay + " 检查时间:" + dateFormat.format(new Date()));
    }

    @Override
    public void visit(AppleElement apple) {
        expCheck(apple, appleExpDay);
    }

    @Override
    public void visit(OrangeElement orange) {
        expCheck(orange,orangeExpDay);
    }

    private void expCheck(FruitElement fruit,int expDay) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        calendar.add(Calendar.DATE,-appleExpDay);
        Date appleExpDate = calendar.getTime();
        if (appleExpDate.after(fruit.getPickDate())) {
            System.out.println(fruit.getName() + " 过期!");
        }
    }
}


/**
 * 新品上市检查
 */
public class NewArrivalVisitor implements Visitor {

    private final Date appleNewDate;

    private final Date orangeNewDate;

    public NewArrivalVisitor(Date appleNewDate, Date orangeNewDate) {
        this.appleNewDate = appleNewDate;
        this.orangeNewDate = orangeNewDate;
        System.out.println("新品上市检查");
    }

    @Override
    public void visit(AppleElement apple) {
        if (appleNewDate.before(apple.getPickDate())) {
            System.out.println(apple.getName() + " 新品上市");
        }
    }

    @Override
    public void visit(OrangeElement orange) {
        if (orangeNewDate.before(orange.getPickDate())) {
            System.out.println(orange.getName() + " 新品上市");
        }
    }
}
/**
 * 水果店结构
 */
public class FruitShopStructure {

    private final List<FruitElement> fruitList = new ArrayList<>();

    public List<FruitElement> getFruitList() {
        return fruitList;
    }

}
public class FruitMarket2 {

    public static void main(String[] args) throws ParseException {
        //新开一家水果店
        FruitShopStructure shop = new FruitShopStructure();
        purchaseFruit(shop);

        System.out.println("------------------------------");
        //市场监督局来检查是否存在过期水果
        DevelopmentVisitor devVisitor1 = new DevelopmentVisitor("市场监督局", 5, 10);
        for (FruitElement element : shop.getFruitList()) {
            element.accept(devVisitor1);
        }
        System.out.println("------------------------------");
        //农业局来检查是否存在过期水果
        DevelopmentVisitor devVisitor2 = new DevelopmentVisitor("农业局", 2, 4);
        for (FruitElement element : shop.getFruitList()) {
            element.accept(devVisitor2);
        }
        System.out.println("------------------------------");
        //检查新品上市
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        NewArrivalVisitor newArrivalVisitor = new NewArrivalVisitor(simpleDateFormat.parse("2023-06-20"), simpleDateFormat.parse("2023-06-21"));
        for (FruitElement element : shop.getFruitList()) {
            element.accept(newArrivalVisitor);
        }
    }

    // 采购水果
    public static void purchaseFruit(FruitShopStructure shop) throws ParseException {
        List<FruitElement> fruitList = shop.getFruitList();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm");
        fruitList.add(new AppleElement("富士康",simpleDateFormat.parse("2023-06-21 12:00")));
        fruitList.add(new OrangeElement("赣南脐橙", simpleDateFormat.parse("2023-05-25 18:00")));
        fruitList.add(new OrangeElement("韶关帝王橘", simpleDateFormat.parse("2023-6-18 21:00")));
        fruitList.add(new AppleElement("王掌柜",simpleDateFormat.parse("2023-06-11 12:00")));
    }
}

图 运行结果 

以上代码是用访问者模式实现文章开头处的需求。

优点

  1. 好的扩展性,能够在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能。
  2. 好的复用性,可以通过访问者来定义整个对象结构通用的功能,从而提高复用程度。
  3. 分离无关行为,通过访问者来分离无关的行为,把相关行为封装在一起,构成一个访问者,这样每个访问者的功能都比较单一。

缺点

1)对象结构变化很困难,不适用于对象结构中的类经常变化的情况,当对象结构发生改变时,访问者的接口及实现都要做相应改变。

2)破坏封装,访问者模式通常需要对象结构开放内部数据给访问者和ObjectStructure,这破坏了对象的封装性。

应用场景

1)需要对一个复杂的数据结构进行操作,并且这些操作可能需要根据不同的元素类型进行变化。

2)当数据结构中的元素种类相对稳定,但可能需要新增一些新的操作时。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

javaWeb之cookiesession

1 回顾 1.1 response对象 一次响应封装对象&#xff0c;由服务器创建。使用response对象将服务器需要的数据发送给浏览器。 将数据存放response对象中&#xff0c;tomcat从response对象获得数据&#xff0c;根据数据组织http响应&#xff0c;最后将http响应内容发送给浏览器&…

selenium自动化教程及使用java来爬取数据

目录 一、介绍二、下载浏览器驱动1.获取要下载的驱动版本号2.下载驱动 三、Maven如下四、简单使用五、定位器1.定位器2.说明(1) class name 定位器(2) css selector 定位器(3) id 定位器(4) name 定位器(5) link text 定位器(6) partial link text 定位器(7) tag 定位器(8) xpa…

第8讲:$.ajax方法使用详解

jQuery对象上面定义了Ajax方法&#xff08;$.ajax()&#xff09;&#xff0c;用来处理Ajax操作。调用该方法后&#xff0c;浏览器就会向服务器发出一个HTTP请求。ajax方法有很多属性&#xff0c;但并非每次调用都要使用所有属性&#xff0c;本讲详细介绍了每个属性的作用&#…

windows搭建vue开发环境

参考博客&#xff1a;最详细的vue安装教程_一只野生程序媛的博客-CSDN博客 Vue安装环境最全教程&#xff0c;傻瓜式安装_浪漫主义码农的博客-CSDN博客 1、安装nodejs&#xff0c;从下面官网下载版本&#xff0c;对应安装就行了&#xff1a; Node.js 中文网 2、安装好后&…

Linux 多路转接 —— poll

目录 传统艺能&#x1f60e;poll&#x1f923;struct pollfd&#x1f923; poll 服务器&#x1f618;PollServer类&#x1f601;运行服务器&#x1f612;事件处理&#x1f601; 服务器测试&#x1f602; 传统艺能&#x1f60e; 小编是双非本科大二菜鸟不赘述&#xff0c;欢迎米…

0基础学习地平线QAT量化感知训练

文章目录 1. 背景2. 基础理论知识3. 文件准备与程序运行4. 代码详解4.1 导入必要依赖4.2 主函数4.3 构建fx模式所需要的float_model4.4 不同阶段模型的获取4.5 定义常规模型训练与验证的函数4.6 float与qat训练代码解读——float_model/qat_model4.7 模型校准部分的代码解读——…

七、docker-compose方式运行Jenkins,更新Jenkins版本,添加npm node环境

docker-compose方式运行Jenkins&#xff0c;更新Jenkins版本&#xff0c;添加npm node环境 一、docker-compose方式安装运行Jenkins 中发现Jenkins版本有点老&#xff0c;没有node环境&#xff0c;本节来说下更新jenkins 及添加构建前端的node环境。 1. 准备好docker-compose…

三种方法将Word文档转换为PDF文件格式

如何将Word文档转换为PDF文件格式呢&#xff1f;大家在传输文件时&#xff0c;很多人喜欢使用PDF文件格式&#xff0c;因为它非常稳定&#xff0c;不会出现格式混乱的问题。但有些人可能不知道如何进行转换&#xff0c;今天我将介绍三种转换方法&#xff0c;让我们一起来学习一…

mysql 删表引出的问题

背景 将测试环境的表同步到另外一个数据库服务器中&#xff0c;但有些表里面数据巨大&#xff0c;&#xff08;其实不同步该表的数据就行&#xff0c;当时没想太多&#xff09;&#xff0c;几千万的数据&#xff01;&#xff01; 步骤 1. 既然已经把数据同步过来的话&#x…

环境配置 | Git的安装及配置[图文详情]

Git是一个开源的分布式版本控制系统&#xff0c;可以有效、高速地处理从小到大的项目版本管理。下面介绍了基础概念及详细的用图文形式介绍一下git安装过程. 目录 1.Git基础概念 2.Git的下载及安装 3.常见的git命令 Git高级技巧 Git与团队协作 1.Git基础概念 仓库&#…

认识异常

目录 异常的概念与体系结构 异常的概念 异常的体系结构 异常的分类 异常的处理 防御式编程 1. LBYL: 2. EAFP: 异常的抛出 异常的捕获 异常声明throws try-catch捕获并处理 关于异常的处理方式 finally 异常的处理流程 自定义异常类 异常的概念与体系结构 异常…

基于SpringBoot+kaptcha的验证码生成

教程 1.添加 Kaptcha 依赖 在 pom.xml 文件中添加 Kaptcha 依赖&#xff1a; <dependency><groupId>com.github.penggle</groupId><artifactId>kaptcha</artifactId><version>2.3.2</version> </dependency> <!--或者 都…

平凯星辰重磅支持 2023 开放原子全球开源峰会,开源数据库分论坛成功召开

2023 年 6 月 11 日至 13 日&#xff0c;以“开源赋能&#xff0c;普惠未来”为主题的 2023 开放原子全球开源峰会开幕式暨高峰论坛在北京成功举办。企业级开源分布式数据库厂商平凯星辰联合创始人兼 CTO 黄东旭受邀出席峰会参与开源论道圆桌&#xff0c;担任开源数据库分论坛出…

第一章 数据可视化简介(复习)

第一章 数据可视化简介 什么是可视化 定义&#xff1a;通过可视表达增强人们完成某些 任务的效率 The American Heritage Dictionary&#xff1a; The act or process of interpreting in visual terms or of putting into visible form&#xff08;用可视形式进行解释的 动作…

VUE L ∠脚手架 配置代理 ⑩⑧

目录 文章有误请指正&#xff0c;如果觉得对你有用&#xff0c;请点三连一波&#xff0c;蟹蟹支持✨ V u e j s Vuejs Vuejs初识 V u e C L I VueCLI VueCLI C L I CLI CLI V u e Vue Vue配置代理 C L I CLI CLI配置方法一 C L I CLI CLI配置方法二 C L I CLI CLI V u …

EL标签-给JSP减负

https://blog.csdn.net/weixin_42259823/article/details/85945149 安装使用 1. 通过命令行创建maven项目 2. 安装jstl包 <dependency><groupId>jstl</groupId><artifactId>jstl</artifactId><version>1.2</version> </depen…

【工程项目管理】工程项目管理实践报告

前言&#xff1a; 1.大学课程的大作业&#xff0c;觉得存起来也没什么用就干脆发出来了。。。 2.很可能有不严谨之处&#xff0c;各位看官如若发现欢迎指出~ 创作者文章管理系统 1 实践环节作业1&#xff1a;选题及任务分解WBS &#xff08;1&#xff09;选题 a.项目名称&a…

【论文阅读】Adap-t: Adaptively Modulating Embedding Magnitude for Recommendation

【论文阅读】Adap-&#x1d70f;: Adaptively Modulating Embedding Magnitude for Recommendation 文章目录 【论文阅读】Adap-&#x1d70f;: Adaptively Modulating Embedding Magnitude for Recommendation1. 来源2. 介绍3. 模型解读3.1 准备工作3.1.1 任务说明3.1.2 基于嵌…

「已解决」已有Umi Antd 环境下安装 formily v2 依赖报错问题

背景 在一个项目中想引入 formily v2 试一下这个针对复杂表单的解决方案&#xff0c;结果发现安装后报错&#xff0c;目前已有的第三方库大致为 “ant-design/icons”: “^5.0.1”, “ant-design/pro-components”: “^2.4.4”, “umijs/max”: “^4.0.68”, “ahooks”: “^3…

textract OCR的安装使用

安装 pip install textract使用 在 Python 中&#xff0c;textract 是一个用于提取文本和信息的库。它提供了一个函数 textract.process()&#xff0c;用于处理不同类型的文档并提取文本内容。下面是 textract.process() 函数的各个参数的介绍&#xff1a; filename&#xf…