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

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

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

访问者模式

定义

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

结构

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

案例

给宠物喂食

  • 访问者角色:给宠物喂食的人
  • 具体访问者角色:主人,其他人
  • 抽象元素角色:动物抽象类
  • 具体元素角色:宠物狗,宠物猫
  • 结构对象角色:主人家
    类图:
    在这里插入图片描述
/*** 抽象元素角色类*/
public interface Animal {// 接受访问者访问的功能void accept(Person person);
}
/*** 具体元素角色类  猫*/
public class Cat implements Animal{@Overridepublic void accept(Person person) {person.feed(this);System.out.println("喵喵喵~");}
}
/*** 具体元素角色类  狗*/
public class Dog implements Animal{@Overridepublic 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{@Overridepublic void feed(Cat cat) {System.out.println("主人喂猫");}@Overridepublic void feed(Dog dog) {System.out.println("主人喂狗");}
}
/*** 具体访问者角色类  别人*/
public class Someone implements Person{@Overridepublic void feed(Cat cat) {System.out.println("别人喂猫");}@Overridepublic 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{@Overridepublic void execute() {System.out.println("dog");}
}public class Cat extends Animal{@Overridepublic 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);   // animalexecute.execute(animal2);  // animalexecute.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);  // animalanimal2.accept(execute); // catanimal3.accept(execute); // dog}
}

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

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

防雷接地工程的概述与行业应用方案

防雷接地工程是指为了保护建筑物、电力设施、通信设施等免受雷电的危害&#xff0c;而采取的一系列技术措施&#xff0c;主要包括接闪器、引下线、接地体等组成的防雷接地系统&#xff0c;以及与之配套的避雷带、避雷网、避雷针、等电位联结、防雷检测等设施。防雷接地工程的目…

Promise执行顺序

小编建议小伙伴们不要跳点看&#xff0c;每一点都是衔接&#xff0c;有比较的 本篇文章考查 ①promise是同步任务还是微任务 ②promise.then()什么时候执行&#xff0c;是微任务还是宏任务 ③如何控制状态变化&#xff0c;不同状态变化&#xff0c;会执行哪个回调函数 1、以下代…

前端性能监控和错误监控

聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…

第二证券:股票交易时间以及规则是什么?

股票生意时间以及规则是什么&#xff1f; 1、股票生意时间 周一至周五上午9&#xff1a;30-11:30&#xff0c;下午13:00-15:00&#xff0c;周末以及法定节假日休市不进行生意。可是不生意不代表不能进行托付&#xff0c;股票在清算之后投资者就能够进行托付。股票的清算时间&…

Can‘‘t connect to MySQL server on localhost (10061)解决方法

参考文章 https://www.jb51.net/article/26505.htmhttps://www.jb51.net/article/26505.htm

什么是HTTP协议?

目录 概念 特点 请求数据格式 请求行 请求头 请求体 响应数据格式 响应行 响应头 响应体 相关的类 HttpServletRequest HttpServletResponse 概念 HTTP全称Hyper Text Transfer Protocol&#xff0c;即超文本传输协议&#xff0c;规定了浏览器和服务器之间数据传…

interface接口(学习推荐版)

接口组成部分 示例代码&#xff1a; 1.默认会在类型前面添加public staic final修饰变量&#xff0c;所以可省略 2.默认在方法前面添加public abstract修饰&#xff0c;但没有staic和final修饰 注意事项&#xff1a; 1、用staic final的变量就是常量 2、接口只能由成员变量&a…

思倍云荣膺2023年度“毕马威中国领先不动产科技企业50”

12月15日&#xff0c;毕马威在上海举办以“聚合力 筑未来”为主题的2023年度“毕马威中国领先不动产科技50”的报告发布会。思倍云荣登2023“毕马威中国领先不动产科技企业50”榜单。 随着AI和大数据的发力、区块链技术的发展和“元宇宙”概念的兴起&#xff0c;数字化正引领着…

ik分词器动态从数据库中加载数据无需重启

ik分词器加载mysql数据库中的热词库 1、下载elasticsearch-analysis-ik 源码包 下载elasticsearch-analysis-ik打开项目(https://github.com/medcl/elasticsearch-analysis-ik) 2、修改插件代码 (1)修改pom.xml中对应版本号 (2)org.wltea.analyzer.dic.Dictionary 单…

Python使用HTTP库发送GET请求的示例——轻松探索网络世界

大家好&#xff0c;今天我要给大家介绍一个非常实用的Python库——HTTP库&#xff0c;它可以帮助我们轻松地发送HTTP请求。今天&#xff0c;我们就来学习一下如何使用HTTP库发送GET请求。 首先&#xff0c;我们需要安装HTTP库。如果你还没有安装&#xff0c;可以通过pip命令进…

开源一个超好用的接口Mock工具——Msw-Tools

作为一名前端开发&#xff0c;是不是总有这样的体验&#xff1a;基础功能逻辑和页面UI开发很快速&#xff0c;本来可以提前完成&#xff0c;但是接口数据联调很费劲&#xff0c;耗时又耗力&#xff0c;有时为了保证进度还不得不加加班。 为了摆脱这种痛苦&#xff0c;经过一周的…

RK3588安装TVM-GPU版本

1.前言 RK3588还有相应的GPU可以使用&#xff0c;我们也可以配置相关的环境&#xff0c;进行GPU的使用 2. RK3588的GPU介绍 Mali-G610 是 Arm 公司开发的第三代 Valhall 架构的 GPU。它于 2022 年 7 月发布&#xff0c;面向中端和高端移动设备。 Mali-G610 采用 Armv9 架构&am…