设计模式—访问者模式

news2024/9/21 12:39:56

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

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)当数据结构中的元素种类相对稳定,但可能需要新增一些新的操作时。

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

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

相关文章

【Unity之IMGUI】—位置信息类和控件基类的封装

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a; ⭐…

出现线程死锁缺陷一般有那些原因?该怎么解决?

&#x1f482; 个人网站:【海拥】【游戏大全】【神级源码资源网】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 寻找学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】 目录 前言什么是线程死锁线程…

【Python】文件操作 ⑤ ( 文件操作 | 以只读模式向已有文件写入数据 | 以追加模式向已有文件写入数据 | 以追加模式打开一个不存在的文件 )

文章目录 一、向文件写出数据1、以只读模式向已有文件写入数据2、以追加模式向已有文件写入数据3、以追加模式打开一个不存在的文件 一、向文件写出数据 1、以只读模式向已有文件写入数据 使用 write 函数向已有文件写入数据 , 会清空该文件中的数据 , 代码展示如下 : file1.t…

当mysql遇上PHP

一.利用PHP连接mySQL数据库 这要从一个故事说起。 某一天&#xff0c;一位名叫MySQL的农夫的一把斧子&#xff08;数据库操作&#xff09;掉进了一条名为PHP的河里&#xff0c;这时候&#xff0c;一位好心的河神出现了 PHP河的河神问他。。。。 下面&#xff0c;咱们还是说正经…

Kubernetes API Server源码学习(三):KubeAPIServer、APIExtensionsServer、AggregatorServer

本文基于Kubernetes v1.22.4版本进行源码学习 9、KubeAPIServer GenericAPIServer提供了一些通用的功能&#xff0c;其他的Server基于GenericAPIServer进行拓展&#xff0c;代码量就会减少许多 KubeAPIServer负责处理Kubernetes内建资源的REST请求&#xff0c;比如Pod、Deploy…

C/C++系列系统学习目录

友情链接&#xff1a;专栏地址 文章目录 一、C语言篇二、C篇 编程规范&#xff1a;C/C语言编程规范 一、C语言篇 章节内容1.初识C语言【C语言篇】初识C语言2.C语言最基础入门【C语言篇】C语言最基础入门3.C语言的输入输出相关知识【C语言篇】C语言的输入/输出相关知识4.C语言…

【Pytest实战】Pytest+Allure+Jenkins自动化测试框架搭建

&#x1f604;作者简介&#xff1a; 小曾同学.com,一个致力于测试开发的博主⛽️&#xff0c;主要职责&#xff1a;测试开发、CI/CD 如果文章知识点有错误的地方&#xff0c;还请大家指正&#xff0c;让我们一起学习&#xff0c;一起进步。&#x1f60a; 座右铭&#xff1a;不想…

从零开始理解Linux中断架构(5)--EL跃迁与Linux用户/内核态

ARM64系统Reset时,PE进入最高的异常级别运行状态 1)Reset后最高异常级别可以选用任何一种运行状态 2)cold reset由输入信号配置,warm reset由RMR_ELx.AA64配置 1)内核态EL1迁移到EL0t 上一节我们提到需要仔细理解的图吗?eret这条特殊的异常返回指令。 我们期望…

《机器学习公式推导与代码实现》chapter14-CatBoost

《机器学习公式推导与代码实现》学习笔记&#xff0c;记录一下自己的学习过程&#xff0c;详细的内容请大家购买作者的书籍查阅。 CatBoost CatBoost是俄罗斯搜索引擎巨头Yandex于2017年开源的一款GBDT计算框架&#xff0c;因能够高效处理数据中的类别特征而取名为CatBoost(C…

一时兴起之matlab学习记录

是学习记录&#xff0c;会有错误的地方 安装的话看其他文章把 小操作 查看历史命令 在输入命令的地方&#xff0c;按下↑的方向键即可 变量 对大小写敏感若想以指定的类型存储就是 类型名(值),如 int16(4)&#xff0c;这个也可以强转变量名字有限制&#xff0c;键入namele…

10分钟搭建Stable Diffusion

前言 人工智能生成内容&#xff08;Artificial Intelligence Generated Content&#xff0c;简称 AIGC&#xff09;是当下最火的概念之一。AIGC 被认为是继专业生成内容&#xff08;Professional Generated Content, PGC&#xff09;和用户生成内容&#xff08;User Generated…

【MySQL】数据库基础 ③

上一章&#xff1a; 【MySQL】数据库基础 ② ✍临时表 说明&#xff1a; MySQL 临时表在我们需要保存一些临时数据时是非常有用的。临时表只在当前连接可见&#xff0c;…

局域网实验报告

计算机网络综合实训 实训报告一 所在院系 计算机与信息工程学院 学科专业名称 计算机科学与技术 导师及职称 柯宗武 教授 提交时间 2023.3.10 网络层实验报告 &#xff08;湖北师范大学计算机与信息工程学院 中国 黄石 435002&#xff09; 1 集线器与交换机的对比实验 1.1 背…

【Python】自动化构建项目结构样式

引言 在使用Python或者其它编程语言的项目时候&#xff0c;编写README.md 往往是不可或缺的&#xff1b; 而在README.md 中&#xff0c;关于项目结构的样式展示&#xff0c;这个是可选的。不展示也无伤大雅&#xff0c;但有展示的话&#xff0c;有以下优点&#xff1a; 提供…

第九章 总结及作业(4)【编译原理】

第九章 总结及作业&#xff08;4&#xff09;【编译原理】 前言推荐第九章 运行时存储空间组织9.1 目标程序运行时的活动9.1.1过程的活动9.1.2参数传递 9.2 运行时存储器的划分9.2.1运行时存储器的划分9.2.2 活动记录9.2.3 存储分配策咯 9.3 静态存储分配9.3.1数据区*9.3.2公用…

基于深度学习的人脸检测技术

用到环境 1、pycharm community edition 2022.3.2 2、Python 3.10 整篇内容都已上传至我的csdn资源中&#xff0c;想用的请移步。 流程 多任务级联卷积神经网络(Multi-task Cascaded Convolutional Networks, MTCNN)算法进行人脸检测 普通人脸检测 单人人脸检测 图1 单人人…

我最喜欢的编程语言是python,以及我的见解!!

这里写目录标题 我最喜欢的编程语言&#xff1a;1、我个人认为编程语言优劣的评选标准2、我对不同编程语言的优点与缺点的拙见**1. Java****2. Python****3. JavaScript****4. C语言&#xff1a;****5. C语言&#xff1a;** 3、对python编程语言未来发展的猜测和未来趋势 我最喜…

Vicuna-13B使用云服务器部署

Vicuna概述 Vicuna由一群主要来自加州大学伯克利分校的研究人员推出&#xff0c;仍然是熟悉的配方、熟悉的味道。Vicuna同样是基于Meta开源的LLaMA大模型微调而来&#xff0c;它的训练数据是来自ShareGPT上的7万多条数据&#xff08;ShareGPT一个分享ChatGPT对话的谷歌插件&am…

kerberos配置dolphinscheduler

kerberos配置dolphinscheduler 一、添加dolphin 用戶1.所有節點上執行如下命令&#xff1a; 二、DolphinScheduler集群模式部署1.集群规划2.前置准备工作3.解压DolphinScheduler安装包4. 创建元数据库及用户5. 配置一键部署脚本6 初始化数据库7.修改common配置文件8. 一键部署D…

华为、思科和瞻博网络三个厂商如何配置基本ACL和高级ACL?

今天给大家带来基本ACL和高级ACL的配置&#xff0c;主要会介绍三个厂商的配置&#xff1a; 其他厂商也可以参考&#xff0c;比如华三的可以参考华为的&#xff0c;锐捷的参考思科的。 1. 基本ACL配置 基本ACL&#xff08;Access Control List&#xff09;是一种简单的网络安全…