【设计模式--行为型--访问者模式】

news2024/11/25 11:02:10

设计模式--行为型--访问者模式

    • 访问者模式
      • 定义
      • 结构
      • 案例
      • 优缺点
      • 使用场景
      • 扩展
        • 分派
        • 动态分派
        • 静态分派
        • 双分派

访问者模式

定义

封装一些作用于某种数据结构中的各元素的操作,它可以在不改变这个数据结构的前提下定义作用于这些元素的新操作。

结构

  • 抽象访问者角色(Visitor):定义了对每一个元素访问的行为,它的参数就是可以访问的元素,它的方法个数理论上来说于元素类个数是一样的,从这点上来看,访问者模式要求元素类的个数不能改变。
  • 具体访问者角色(Concrete Visitor):给出对每一个元素类访问时所产生的具体行为。
  • 抽象元素角色(Element):定义了一个接受访问者的方法,其意义是指,每一个元素都要可以被访问者访问。
  • 具体元素角色(Concrete Element):提供接受访问方法的具体实现,而这个具体的实现,通常情况下是使用访问者提供的访问该元素类的方法。
  • 对象结构角色(Object Structure):定义当中所提到的对象结构,对象机构是一个抽象表述,具体点可以理解为一个具有容器性质或者复合对象特性的类,它会含有一组元素,并且可以迭代这些元素,供访问者访问。

案例

给宠物喂食

  • 访问者角色:给宠物喂食的人
  • 具体访问者角色:主人,其他人
  • 抽象元素角色:动物抽象类
  • 具体元素角色:宠物狗,宠物猫
  • 结构对象角色:主人家
    类图:
    在这里插入图片描述
/**
 * 抽象元素角色类
 */
public interface Animal {

    // 接受访问者访问的功能
    void accept(Person person);
}
/**
 * 具体元素角色类  猫
 */
public class Cat implements Animal{
    @Override
    public void accept(Person person) {
        person.feed(this);
        System.out.println("喵喵喵~");
    }
}
/**
 * 具体元素角色类  狗
 */
public class Dog implements Animal{
    @Override
    public void accept(Person person) {
        person.feed(this);
        System.out.println("汪汪汪~");
    }
}
/**
 * 抽象访问者角色类
 */
public interface Person {
    void feed(Cat cat);
    void feed(Dog dog);
}
/**
 * 具体访问者角色类  自己
 */
public class Owner implements Person{
    @Override
    public void feed(Cat cat) {
        System.out.println("主人喂猫");
    }

    @Override
    public void feed(Dog dog) {
        System.out.println("主人喂狗");
    }
}
/**
 * 具体访问者角色类  别人
 */
public class Someone implements Person{
    @Override
    public void feed(Cat cat) {
        System.out.println("别人喂猫");
    }

    @Override
    public void feed(Dog dog) {
        System.out.println("别人喂狗");
    }
}
/**
 * 对象结构类
 */
public class Home {

    // 声明一个集合对象,用来存储元素对象
    private List<Animal> nodeList = new ArrayList<>();

    // 添加元素
    public void add(Animal animal){
        nodeList.add(animal);
    }
    public void action(Person person){
        // 遍历集合获取每一个元素,让访问者访问每一个元素
        for (Animal animal : nodeList) {
            animal.accept(person);
        }
    }
}
public class Test01 {
    public static void main(String[] args) {
        // 创建home对象
        Home home = new Home();
        // 添加元素
        home.add(new Cat());
        home.add(new Dog());
        // 创建主人对象
        Owner owner = new Owner();
        // 让主人喂所有的宠物
        home.action(owner);
    }
}

在这里插入图片描述

优缺点

  • 优点
    • 扩展性好,在不修改对象结构中元素的情况下,为对象结构中的元素添加新的功能。
    • 复用性好,通过访问者来定义整个对象结构通用的功能,从而提高复用程度。
    • 分离无关行为,通过访问者来分离无关的行为,把相关的行为封装在一起,构成一个访问者,这样每一个访问者的功能都比较单一。
  • 缺点
    • 对象结构变化困难,在访问者模式中,每增加一个新的元素类,都要在每一个具体访问者类中增加相应的具体操作,违背了开闭原则
    • 违反了依赖倒置原则,访问者模式依赖了具体类,而没有依赖抽象类。

使用场景

  • 对象结构相对稳定,但操作算法经常变化
  • 对象结构中的对象需要提供多种不同且不相关的操作,而且要避免让这些操作的变化影响对象的结构。

扩展

访问者用到了一种双分派技术。

分派

变量被声明时的类型叫做变量的静态类型,又称明显类型;而变量所引起的对象的真实类型又叫做变量的实际类型。比如Map map = new HashMap(),map变量的静态类型是Map,实际类型是HashMap。根据对象的类型而对方法进行的选择。就是分派(Dispatch),分派又分两种,静态分派和动态分派。

  • 静态分派,发生在编译时期,分派根据静态类型信息发生。方法重载就是静态分派。
  • 动态分派,发生在运行时期,动态分派动态的置换掉某个方法,Java就是通过方法的重写支持动态分派。
动态分派

通过方法的重写支持动态分派

public class Animal {
    public void execute(){
        System.out.println("animal");
    }
}

public class Dog extends Animal{
    @Override
    public void execute() {
        System.out.println("dog");
    }
}

public class Cat extends Animal{
    @Override
    public void execute() {
        System.out.println("cat");
    }
}

public class Test{
    public static void main(String[] args) {
        Animal animal = new Dog();
        animal.execute();

        Animal animal1 = new Cat();
        animal1.execute();
    }
}

上面是多态,运行执行的是子类中的方法。
Java编译器在编译时期并不总是知道哪些代码会被执行,因为编译器仅仅知道对象的静态类型,而不知道对象的真是类型;而方法的调用则是根据对象的真实类型,而不是静态类型。

静态分派

通过方法重载支持静态分派

public class Animal {
    public void execute(){
        System.out.println("animal");
    }
}

public class Dog extends Animal{
}

public class Cat extends Animal{
}

public class Execute{
    public void execute(Animal animal){
        System.out.println("animal");
    }
    public void execute(Cat cat){
        System.out.println("cat");
    }
    public void execute(Dog dog){
        System.out.println("dog");
    }
}

public class Test{
    public static void main(String[] args) {
        Animal animal = new Animal();
        Animal animal2 = new Cat();
        Animal animal3 = new Dog();
        
        Execute execute = new Execute();
        execute.execute(animal);   // animal
        execute.execute(animal2);  // animal
        execute.execute(animal3);  // animal
    }
}

重载方法的分派是根据静态类型进行的,这个分派过程在编译时期就完成了。

双分派

在选择一个方法的时候,不仅仅要根据消息接收者的运行时区别,还要根据参数的运行时区别

public class Animal {
    public void accept(Execute execute){
        execute.execute(this);
    }
}

public class Dog extends Animal{
    public void accept(Execute execute) {
        execute.execute(this);
    }
}

public class Cat extends Animal{
    public void accept(Execute execute) {
        execute.execute(this);
    }
}

public class Execute{
    public void execute(Animal animal){
        System.out.println("animal");
    }
    public void execute(Cat cat){
        System.out.println("cat");
    }
    public void execute(Dog dog){
        System.out.println("dog");
    }
}

public class Test{
    public static void main(String[] args) {
        Animal animal = new Animal();
        Animal animal2 = new Cat();
        Animal animal3 = new Dog();

        Execute execute = new Execute();
        animal.accept(execute);  // animal
        animal2.accept(execute); // cat
        animal3.accept(execute); // dog
    }
}

上面代码中,客户端将Execute对象作为参数传递给Animal类型的变量调用的方法,这里完成第一次分派,这里是方法重写,所以是动态分派,也就是执行实际类型中的方法,同时也是将自己this作为参数传递进去,这里就完成了第二次分派,这里的Execute类中有多个重载的方法,而传递进行的是this,就是具体的实际类型的对象。
双分派实现动态绑定的本质,就是在重载方法委派的前面加上了继承体系中覆盖的环节,由于覆盖是动态的,所以重载就是动态的了。

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

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

相关文章

故障排除: Vcenter Root user password expires in 0 days.

故障排除: Vcenter Root user password expires in 0 days. 1. 故障现象 登录Vsphere Client显示报错信息如下: Vcenter Root user password expires in 0 days.2. 故障排除 2.1 打开Vcenter ssh 访问VMware vCenter Server 管理,即5480端口,比如VMware vSphere地址是https…

RAG(检索增强生成)技术

1.基本概念 检索增强 LLM ( Retrieval Augmented LLM )&#xff0c;简单来说&#xff0c;就是给 LLM 提供外部数据库&#xff0c;对于用户问题 ( Query )&#xff0c;通过一些信息检索 ( Information Retrieval, IR ) 的技术&#xff0c;先从外部数据库中检索出和用户问题相关…

【个人版】SpringBoot下Spring-Security自定义落地篇【四】

SpringBoot Spring-Security 背景&#xff1a; 上篇文章在源码读取的基础上&#xff0c;根据自身代码习惯及需求&#xff0c;总结了一个自定义简单落地版本。后来在看到松哥写的博文&#xff08;不太爱看官网&#xff09;&#xff0c;发现还有新的变种模式&#xff0c;虽然整…

机器学习---决策树

介绍 决策树和随机森林都是非线性有监督的分类模型。 决策树是一种树形结构,树内部每个节点表示一个属性上的测试,每个分支代表一个测试输出,每个叶子节点代表一个分类类别。通过训练数据构建决策树,可以对未知数据进行分类, 随机森林是由多个决策树组成,随机森林中每…

【参天引擎】华为参天引擎内核架构源码架构,多线程服务,数据节点管理,多节点间元数据管理

cantian引擎源码结构 ​专栏内容&#xff1a; 参天引擎内核架构 本专栏一起来聊聊参天引擎内核架构&#xff0c;以及如何实现多机的数据库节点的多读多写&#xff0c;与传统主备&#xff0c;MPP的区别&#xff0c;技术难点的分析&#xff0c;数据元数据同步&#xff0c;多主节点…

极智AI | 算子融合、矩阵分块 一图看懂大模型优化技术FlashAttention

欢迎关注我的公众号 [极智视界],获取我的更多经验分享 大家好,我是极智视界,本文来介绍一下 算子融合、矩阵分块 一图看懂大模型优化技术FlashAttention。 邀您加入我的知识星球「极智视界」,星球内有超多好玩的项目实战源码下载,链接:https://t.zsxq.com/0aiNxERDq 没错…

RabbitMQ入门指南(二):架构和管理控制台的使用

专栏导航 RabbitMQ入门指南 从零开始了解大数据 目录 专栏导航 前言 一、RabbitMQ架构 二、RabbitMQ管理控制台的使用 1.Exchange交换机 2.Queue队列 3.绑定Exchange交换机和Queue队列 4.发送消息 5.数据隔离 总结 前言 RabbitMQ是一个高效、可靠的开源消息队列系统…

HTML_CSS的基本选择器的使用及其作用范围和优先级

目录 ✨CSS的使用&#xff1a;行内样式内部样式外部样式 ✨CSS基本选择器&#xff1a;id选择器class选择器标签选择器 ✨优先级&#xff1a;选择器的优先级样式表的优先级 ✨CSS的使用&#xff1a; 根据定义CSS的位置不同&#xff0c;分为行内样式、内部样式和外部样式 行内样…

java配置+J_IDEA配置+git配置+maven配置+基本语句

当前目录文件夹dir 进入文件夹cd 返回上一级cd.. 创建文件夹&#xff1a;mkdir 文件名删除文件夹&#xff1a;rd 文件夹名&#xff0c; 目录不为空不能直接删 rd /s 带子文件夹一起删 清屏cls 切换d盘才能进入 下载git地址&#xff1a; Git - Downloading Package (g…

隐私计算介绍

这里只对隐私计算做一些概念性的浅显介绍&#xff0c;作为入门了解即可 目录 隐私计算概述隐私计算概念隐私计算背景国外各个国家和地区纷纷出台了围绕数据使用和保护的公共政策国内近年来也出台了数据安全、隐私和使用相关的政策法规 隐私计算技术发展 隐私计算技术安全多方计…

JDBC 数据库连接池

目录 一、什么是数据库连接池二、为什么需要数据库连接池&#xff1f;三、JDBC 数据库连接池的实现四、C3P0的使用1、加入c3p0 jar包2、配置xml文件3、c3p0-config.xml模板4、C3P0的使用 五、Druid的使用1、加入Druid jar包2、定义配置文件:3、Druid连接池的使用 六、HikariCP的…

【最新】2023年30米分辨率土地利用遥感监测数据

改革开放以来&#xff0c;中国经济的快速发展对土地利用模式产生了深刻的影响。同时&#xff0c;中国又具有复杂的自然环境背景和广阔的陆地面积&#xff0c;其土地利用变化不仅对国家发展&#xff0c;也对全球环境变化产生了深刻的影响。为了恢复和重建我国土地利用变化的现代…

硬件基础-二极管

3.二极管 正偏时是多数载流子载流导电&#xff0c;反偏时是少数载流子载流导电。所以&#xff0c;正偏电流大&#xff0c;反偏电流小&#xff0c;PN 结显示出单向电性。多数载流子正向通过 PN 结时就需要克服内电场的作用&#xff0c;需要约 0.7 伏的外加电压&#xff0c;这是…

windows10录屏快捷键,让你效率翻倍!

“大家知道&#xff0c;windows 10系统有录屏快捷键吗&#xff1f;每次都要通过搜索才能打开&#xff0c;感觉花费的时间太多了&#xff0c;要是可以快速打开就方便多了&#xff0c;所以有人知道windows10系统的录屏快捷键是什么吗&#xff1f;” 在windows 10系统中&#xff…

OpenCV-8RGB和BGR颜色空间

一. RGB和BGR 最常见的色彩空间就是RGB&#xff0c;人眼也是基于RGB的色彩空间去分辨颜色。 OpenCV默认使用的是BGR. BGR和RGB色彩空间的区别在于图片在色彩通道上的排列顺序不同。 二.HSV, HSL和YUV 1.HSV(HSB) OpenCV用的最多的色彩空间是HSV. Hue&#xff1a;色相&…

【玩转 TableAgent 数据智能分析】股票交易数据分析+预测

文章目录 一、什么是TableAgent二、TableAgent 的特点三、实践前言四、实践准备4.1 打开官网4.2 注册账号4.3 界面介绍4.4 数据准备 五、确认分析需求六、TableAgent体验七、分析结果解读八、总结&展望 一、什么是TableAgent TableAgent是一款面向企业用户的智能数据分析工…

Pytorch深度强化学习案例:基于Q-Learning的机器人走迷宫

目录 0 专栏介绍1 Q-Learning算法原理2 强化学习基本框架3 机器人走迷宫算法3.1 迷宫环境3.2 状态、动作和奖励3.3 Q-Learning算法实现3.4 完成训练 4 算法分析4.1 Q-Table4.2 奖励曲线 0 专栏介绍 本专栏重点介绍强化学习技术的数学原理&#xff0c;并且采用Pytorch框架对常见…

AntDB-T提升查询性能的关键之查询优化解析

查询优化器是提升查询效率非常重要的手段&#xff0c;本文将主要介绍AntDB-T数据库查询优化的相关设计。AntDB-T数据库是一款企业级通用分布式关系型数据库&#xff0c;而查询是AntDB-T数据库管理系统中最关键、最吸引人的功能之一。每个生产数据库系统每天都需要处理大量的查询…

python【matplotlib】鼠标拖动滚动缩放坐标范围和拖动图例共存

背景 根据前面的博文&#xff1a; python【matplotlib】画图鼠标缩放拖动动态改变坐标轴范围 和Python【Matplotlib】图例可拖动改变位置 两个博文&#xff0c;博主考虑了一下&#xff0c;如何将两者的功能结合起来&#xff0c;让二者共存。 只需根据Python【Matplotlib】鼠标…

【音视频 | AAC】AAC音频编码详解

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; &#x1f923;本文内容&#x1f923;&a…