小侃设计模式(廿二)-访问者模式

news2024/10/7 6:40:28

1.概述

访问者模式(Visitor Pattern)指的是在类的内部结构不变的情况下,不同的访问者访问这个对象都会呈现出不同的处理方式。它的主要作用时将数据结构与数据操作分离,将不同的算法与其所作用的对象进行分离。本文将详述访问者模式的原理及其使用方式。

2.原理及使用

2.1 原理

访问者UML类图如下所示:
在这里插入图片描述
类图中主要包含五个核心元素:抽象访问者接口(Visitor)、具体访问者类(ConcreteVisitorA、ConcreteVisitorB)、抽象元素接口(Element)、具体元素类(ConcreteElementA、ConcreteElementB)、对象结构类(ObjectStructure)。具体解释如下:

抽象访问者接口(Visitor):接口或抽象类,定义了对每个Element访问行为,逻辑上它内部的方法个数与元素的个数是一致的,正常情况下,Visitor内部元素的类型相对稳定,如果该接口需要经常变更,说明模式选择有问题,可能更适合策略模式或其他模式;
具体访问者类(ConcreteVisitorA、ConcreteVisitorB):实现抽象接口Visitor,内部实现访问者访问不同元素的处理逻辑;
抽象元素接口(Element):接口或抽象类,它定义了一个accept()方法,用于调用访问者本身,实现具体逻辑;
具体元素类(ConcreteElementA、ConcreteElementB):实现抽象元素(Element),accept()方法大多数直接调用Visitor,可增加具体的处理逻辑;
对象结构类(ObjectStructure):负责关联访问者和元素对象,存储元素对象,并提供访问元素的方法,一般会有accept()方法来解释访问者对象。

2.2 案例

现在有个超市,叫胖东西,顾客可以办理不同类型的会员卡(铂金会员、黄金会员、白银会员和普通会员),到了年底,不同类型的会员买年货(猪肉、牛奶等)会有不同的折扣(铂金会员5折,黄金会员7折,白银会员9折,普通会员不打折)。
根据访问者模式,画出UML类图如下:
在这里插入图片描述
代码如下:

public interface Goods {

    double accept(Member member);

    void display();

}

public class Milk implements Goods{

    private String name="天真蓝水牛奶";

    private double price;

    private int number;

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
    }

    public Milk(double price, int number) {
        this.price = price;
        this.number = number;
    }

    @Override
    public double accept(Member member) {
        return member.visit(this);
    }

    @Override
    public void display() {
        System.out.println(name + ",价格:" + price + ",数量:" + number);
    }
}
public class Pork implements Goods {

    private String name = "进口黑猪肉";

    private double price;

    private int number;

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
    }

    public Pork(double price, int number) {
        this.price = price;
        this.number = number;
    }

    @Override
    public double accept(Member member) {
        return member.visit(this);
    }

    @Override
    public void display() {
        System.out.println(name + ",价格:" + price + ",数量:" + number);
    }
}

public class ObjectStructure {

    private static List<Goods> goods = new ArrayList<>();

    //计算总价
    public double accept(Member member) {
        double sum = goods.stream().mapToDouble(goods1 -> goods1.accept(member)).sum();
        return sum;
    }

    public void add(Goods good) {
        goods.add(good);
    }

    public void remove(Goods good) {
        goods.remove(good);
    }

}

public interface Member {

    //铂金会员折扣价
    double visit(Pork pork);

    //黄金会员折扣价
    double visit(Milk milk);


}

public class Platinum implements Member {

    @Override
    public double visit(Pork pork) {
        return 0.6 * pork.getPrice() * pork.getNumber();
    }

    @Override
    public double visit(Milk milk) {
        return 0.6 * milk.getPrice() * milk.getNumber();
    }
}

public class Gold implements Member {
    @Override
    public double visit(Pork pork) {
        return 0.7 * pork.getPrice() * pork.getNumber();
    }

    @Override
    public double visit(Milk milk) {
        return 0.7 * milk.getPrice() * milk.getNumber();
    }
}

public class Silver implements Member{
    @Override
    public double visit(Pork pork) {
        return 0.9 * pork.getPrice() * pork.getNumber();
    }

    @Override
    public double visit(Milk milk) {
        return 0.9 * milk.getPrice() * milk.getNumber();
    }
}

public class Normal implements Member {

    @Override
    public double visit(Pork pork) {
        return pork.getPrice() * pork.getNumber();
    }

    @Override
    public double visit(Milk milk) {
        return milk.getPrice() * milk.getNumber();
    }

}

public class Client {

    public static void main(String[] args) {
        //铂金会员
        Platinum platinum = new Platinum();
        //黄金会员
        Gold gold = new Gold();
        //白银
        Silver silver = new Silver();
        //普通
        Normal normal = new Normal();

        ObjectStructure objectStructure = new ObjectStructure();
        //买10箱牛奶
        Goods milk = new Milk(129.0, 10);
        //买10斤猪肉
        Goods pork = new Pork(108.0, 10);

        objectStructure.add(milk);
        objectStructure.add(pork);

        double accept = objectStructure.accept(platinum);
        System.out.println("铂金会员,买10箱牛奶和10斤进口猪肉,总价为:" + accept);

        double accept1 = objectStructure.accept(gold);
        System.out.println("黄金会员,买10箱牛奶和10斤进口猪肉,总价为:" + accept1);

        double accept2 = objectStructure.accept(silver);
        System.out.println("白银会员,买10箱牛奶和10斤进口猪肉,总价为:" + accept2);

        double accept3 = objectStructure.accept(normal);
        System.out.println("普通会员,买10箱牛奶和10斤进口猪肉,总价为:" + accept3);

    }
}


运行结果如下:

在这里插入图片描述

2.3 优点和缺点

2.3.1 优点

1.符合单一职责原则,让程序具有优秀的扩展性、灵活性;
2.访问者模式适用于数据结构相对稳定的系统,可以用来做过滤器与拦截器之类的功能。

2.3.2 缺点

1.违背迪米特法则,访问者关注了其它类的内部细节;
2.违背了依赖倒置原则,访问者访问的是具体元素,而不是抽象元素。

3.小结

1.访问者模式是设计模式中较为复杂的一种模式,主要适用于对象对应的类改变较少,同时需要对对象进行较多操作的场景;
2.在某种程度上,访问者模式与策略模式和状态模式有一定相似性,策略模式是抽象算法类,让子类去实现具体算法,根据条件进行算法替换;状态模式是将状态封装到对象中,将状态与行为结合;访问者则是将不同访问者对应的操作封装到类中,每个类中都有策略。

4.参考文献

1.《设计模式之禅》-秦小波著
2.《大话设计模式》-程杰著
3.https://www.bilibili.com/video/BV1G4411c7N4-尚硅谷设计模式
4.https://www.runoob.com/design-pattern/observer-pattern.html

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

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

相关文章

DW动手学数据分析Task2:数据清洗及特征处理

文章目录一 数据清洗1 缺失值观察与处理1.1 缺失值观察1.2 缺失值处理2 重复值观察与处理二 特征处理1 分箱&#xff08;离散化&#xff09;处理2 对文本变量进行转换3 从纯文本Name特征里提取出Titles的特征3 参考文章一 数据清洗 数据清洗&#xff1a;我们拿到的数据通常是不…

树的知识概括锦囊(一)

作者&#xff1a;爱塔居 专栏&#xff1a;数据结构 作者简介&#xff1a;大三学生&#xff0c;希望跟大家一起进步&#xff01; 文章目录 目录 文章目录 一、树形结构 二、树的基础知识 三、二叉树 3.1 概念 3.2 特殊的二叉树 ​编辑 3.3 二叉树的性质 四、习题挑战 一、树形结…

手把手教你学51单片机-如何学习单片机

大多数大学生之所以最后变的平庸,不是因为脑子多么笨,也不是全怪自己贪玩不上进,只是没有一个好的领路人,许多学校可能挂着导师的名头,但是多数是挂羊头卖狗肉或者是干脆不管。最后等大学生毕业之后,那些所谓的老师就会说学生很差或者学习很差,反正就是跟自己没啥关系。…

OSPF综合实验(华为)

题目&#xff1a; 思路&#xff1a; 首先配置每个区域的路由和环回地址&#xff0c;其次&#xff0c;根据题目要求打通每个网络的连接&#xff0c;区域0用MGRE打通网络&#xff0c;区域4需要重发布&#xff0c;其次再考虑优化的问题。ip地址的规划是为了更好的路由汇总&#x…

《 Unity Shader 入门精要》第5章 开始 Unity Shader 学习之旅

第5章 开始 Unity Shader 学习之旅 5.2 一个最简单的顶点/片元着色器 顶点/片元着色器的基本结构 // Upgrade NOTE: replaced mul(UNITY_MATRIX_MVP,*) with UnityObjectToClipPos(*)// 定义 shader 的名字 Shader "Chapter 5/Simple Shader" {SubShader{Pass {//…

自动驾驶控制算法之车辆横向控制(project)

本文为深蓝学院-自动驾驶控制与规划-第三章作业 目录 1 projection introduction 2 思路提示 2.1 ComputeControlCmd 2.2 ComputeLateralErrors 3 Corer Case 3.1 Low speed operation 3.2 Extra damping on heading 3.3 Steer into constant radius curve 4 ROSLGSV…

FFmpeg-4.2.4的filter: drawbox源码分析

1. vf_drawbox.c功能 有两个功能 ,添加方框,和添加网格; 1.1 添加方框效果 1.2 添加网格效果

c++数据结构-树(详细总结附代码,一看就懂)

树的定义一棵树是由n&#xff08;n>0&#xff09;个元素组成的有限集合&#xff0c;其中&#xff1a;&#xff08;1&#xff09;每个元素称为结点&#xff08;node&#xff09;&#xff08;2&#xff09;有一个特定的结点&#xff0c;称为根结点或树根&#xff08;root&…

上海亚商投顾:双创指数低开高走,数字经济继续活跃

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。市场情绪三大指数今日低开高走&#xff0c;创业板指午后涨超1%&#xff0c;科创50指数涨超1.6%。数字经济概念继续爆发&#…

算法基础(一):时间复杂度和空间复杂度

算法基础&#xff08;一&#xff09;&#xff1a;时间复杂度和空间复杂度时间复杂度 O(1)O(1)O(1) O(N)O(N)O(N) O(logN)O(log N)O(logN) O(MN)O(MN)O(MN) O(NlogN)O(Nlog N)O(NlogN)、O(MlogN)O(Mlog N)O(MlogN) O(N2)O(N^2)O(N2)空间复杂度一些算法基础知识点和leetcode题解&…

【网络通信】【电信运营商实战工程师】思科设备篇-思科设备企业网实战

电信运营商实战工程师系列文章. 思科设备篇-思科设备企业网实战. 文章目录1. 思科设备基本开局配置2. ARP协议、交换机工作原理及广播风暴问题3. 思科设备 VLAN 及单臂路由实战4. 思科三层交换机实现 VLAN 间路由实战5. 思科设备静态默认及浮动路由实战6. 思科设备NAT实战全集1…

YOLO_V8训练自己的数据集

YOLO_V8在2023年开年横空出世&#xff0c;在春节前还得卷一下。由于YOLO_V8和YOLO_V5是同一个作者&#xff0c;所以很多操作都是一样的&#xff0c;下面主要描述一下如何用自己的数据集进行训练和测试&#xff08;非命令行的方式&#xff09;。1、训练数据和模型的目录结构这里…

设计模式学习(九):Abstract Factory抽象工厂模式

目录 一、什么是Abstract Factory模式 二、Abstract Factory示例代码 2.1 类之间的关系 2.2 抽象的零件:ltem类 2.3 抽象的零件:Link类 2.4 抽象的零件:Tray类 2.5 抽象的产品: Page类 2.6 抽象的工厂:Factory类 2.7 使用工厂将零件组装称为产品:Main类 2.8 具体的工厂…

linux三剑客之AWK

目录 AWK是什么 AWK基本结构 a.txt的文本实例 AWK内置变量 a.txt的文本实例 AWK自定义变量 a.txt的文本实例 AWK内置函数 a.txt的文本实例 awk高级输出 a.txt的文本实例 排序输出 a.txt的文本实例 条件选择输出 a.txt的文本实例 控制语句 a.txt的文本实例 AWK是什…

Java SE 继承和多态

继承和多态 1. 继承 1.1 为什么需要继承 Java中使用类对现实世界中实体来进行描述&#xff0c;类经过实例化之后的产物对象&#xff0c;则可以用来表示现实中的实体&#xff0c;但是 现实世界错综复杂&#xff0c;事物之间可能会存在一些关联&#xff0c;那在设计程序是就需…

Elasticsearch7.8.0版本高级查询—— 指定查询字段查询文档

目录一、初始化文档数据二、指定查询字段查询文档2.1、概述2.2、示例一、初始化文档数据 在 Postman 中&#xff0c;向 ES 服务器发 POST 请求 &#xff1a;http://localhost:9200/user/_doc/1&#xff0c;请求体内容为&#xff1a; {"name":"张三","…

Git知识学习

主要内容&#xff1a;熟练掌握Git、GitHub、GitLab、Gitee码云的使用 文章目录1.Git概述1.1版本控制1.2版本控制工具1.3Git和代码托管中心2.Git常用命令2.1设置用户签名2.2初始化本地库2.3查看本地库状态2.3.1首次查看2.3.2新增文件2.3.3再次查看2.4添加暂存区2.4.1将工作区文件…

! LaTeX Error: File xxx.sty not found-统一解决办法

在使用一些模板时常见这个错&#xff0c;其实就是缺宏包&#xff01;解决方案如下&#xff01; 第一步&#xff1a;在网站 https://ctan.org/pkg 找到你缺失的宏包&#xff0c;下载zip文件。 第二步&#xff1a;将解压后的文件夹复制到安装路径下&#xff1a; 如&#xff…

aws ecs 理解任务和容器的资源分配

参考资料 如何在 Amazon ECS 中为任务分配内存&#xff1f; 关于 Amazon ECS 中的 CPU 分配&#xff0c;我需要了解哪些信息&#xff1f; Amazon ECS CloudWatch 指标 任务定义参数 在ecs中可以指定资源的分配逻辑&#xff0c;其实就是cpu和内存分配。 下面这张图对ecs任…

搜索本地文件

李国春 处理大量的数据集时将文件整理到一起也是一个重要的工作。本文介绍一个将本地计算机目标文件的绝对路径汇集到一个文本文件的中的脚本。以方便后续批量处理这些文件。 启动RSD&#xff0c;在脚本编辑窗口输入图1中的代码。点击工具条上的小三角开始运行&#xff0c;提…