第二十一章 访问者模式

2024-06-17 14:36

本文主要是介绍第二十一章 访问者模式,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

1 访问者模式介绍

2 访问者模式原理

 3 访问者模式实现

4 访问者模式总结


1 访问者模式介绍

访问者模式(Visitor Pattern) 的原始定义是:允许在运行时将一个或多个操作应用于一组对象,将操作与对象结构分离

2 访问者模式原理

  • 抽象访问者(Visitor)角色:可以是接口或者抽象类,定义了一系列操作方法,用来处理所有数据元素,通常为同名的访问方法,并以数据元素类作为入参来确定那个重载方法被调用.
  • 具体访问者(ConcreteVisitor)角色:访问者接口的实现类,可以有多个实现,每个访问者都需要实现所有数据元素类型的访问重载方法.
  • 抽象元素(Element)角色:被访问的数据元素接口,定义了一个接受访问者的方法(accept),其意义是指,每一个元素都要可以被访问者访问。
  • 具体元素(ConcreteElement)角色: 具体数据元素实现类,提供接受访问方法的具体实现,而这个具体的实现,通常情况下是使用访问者提供的访问该元素类的方法,其accept实现方法中调用访问者并将自己 "this" 传回。
  • 对象结构(Object Structure)角色:包含所有可能被访问的数据对象的容器,可以提供数据对象的迭代功能,可以是任意类型的数据结构.
  • 客户端 ( Client ) : 使用容器并初始化其中各类数据元素,并选择合适的访问者处理容器中的所有数据对象.

 3 访问者模式实现

/*** 抽象商品父类* @author spikeCong* @date 2022/10/18**/
public abstract class Product {private String name;  //商品名private LocalDate producedDate;  // 生产日期private double price;  //单品价格public Product(String name, LocalDate producedDate, double price) {this.name = name;this.producedDate = producedDate;this.price = price;}public String getName() {return name;}public void setName(String name) {this.name = name;}public LocalDate getProducedDate() {return producedDate;}public void setProducedDate(LocalDate producedDate) {this.producedDate = producedDate;}public double getPrice() {return price;}public void setPrice(double price) {this.price = price;}
}/*** 糖果类* @author spikeCong* @date 2022/10/18**/
public class Candy extends Product{public Candy(String name, LocalDate producedDate, double price) {super(name, producedDate, price);}
}/*** 酒水类* @author spikeCong* @date 2022/10/18**/
public class Wine extends Product{public Wine(String name, LocalDate producedDate, double price) {super(name, producedDate, price);}
}/*** 水果类* @author spikeCong* @date 2022/10/18**/
public class Fruit extends Product{//重量private float weight;public Fruit(String name, LocalDate producedDate, double price, float weight) {super(name, producedDate, price);this.weight = weight;}public float getWeight() {return weight;}public void setWeight(float weight) {this.weight = weight;}
}

访问者接口

/*** 访问者接口-根据入参不同调用对应的重载方法* @author spikeCong* @date 2022/10/18**/
public interface Visitor {public void visit(Candy candy);  //糖果重载方法public void visit(Wine wine);  //酒类重载方法public void visit(Fruit fruit);  //水果重载方法
}

具体访问者

/*** 折扣计价访问者类* @author spikeCong* @date 2022/10/18**/
public class DiscountVisitor implements Visitor {private LocalDate billDate;public DiscountVisitor(LocalDate billDate) {this.billDate = billDate;System.out.println("结算日期: " + billDate);}@Overridepublic void visit(Candy candy) {System.out.println("糖果: " + candy.getName());//获取产品生产天数long days = billDate.toEpochDay() - candy.getProducedDate().toEpochDay();if(days > 180){System.out.println("超过半年的糖果,请勿食用!");}else{double rate = 0.9;double discountPrice = candy.getPrice() * rate;System.out.println("糖果打折后的价格"+NumberFormat.getCurrencyInstance().format(discountPrice));}}@Overridepublic void visit(Wine wine) {System.out.println("酒类: " + wine.getName()+",无折扣价格!");System.out.println("原价: "+NumberFormat.getCurrencyInstance().format(wine.getPrice()));}@Overridepublic void visit(Fruit fruit) {System.out.println("水果: " + fruit.getName());//获取产品生产天数long days = billDate.toEpochDay() - fruit.getProducedDate().toEpochDay();double rate = 0;if(days > 7){System.out.println("超过七天的水果,请勿食用!");}else if(days > 3){rate = 0.5;}else{rate = 1;}double discountPrice = fruit.getPrice() * fruit.getWeight() * rate;System.out.println("水果价格: "+NumberFormat.getCurrencyInstance().format(discountPrice));}public static void main(String[] args) {LocalDate billDate = LocalDate.now();Candy candy = new Candy("徐福记",LocalDate.of(2022,10,1),10.0);System.out.println("糖果: " + candy.getName());double rate = 0.0;long days = billDate.toEpochDay() - candy.getProducedDate().toEpochDay();System.out.println(days);if(days > 180){System.out.println("超过半年的糖果,请勿食用!");}else{rate = 0.9;double discountPrice = candy.getPrice() * rate;System.out.println("打折后的价格"+NumberFormat.getCurrencyInstance().format(discountPrice));}}
}

客户端

public class Client {public static void main(String[] args) {//德芙巧克力,生产日期2002-5-1 ,原价 10元Candy candy = new Candy("德芙巧克力",LocalDate.of(2022,5,1),10.0);Visitor visitor = new DiscountVisitor(LocalDate.of(2022,10,11));visitor.visit(candy);}
}

由于访问者的重载方法只能对当个的具体商品进行计价,如果顾客选择了多件商品来结账时,就可能会引起重载方法的派发问题(到底该由谁来计算的问题).

/*** 接待者接口(抽象元素角色)* @author spikeCong* @date 2022/10/18**/
public interface Acceptable {//接收所有的Visitor访问者的子类实现类public void accept(Visitor visitor);
}/*** 糖果类* @author spikeCong* @date 2022/10/18**/
public class Candy extends Product implements Acceptable{public Candy(String name, LocalDate producedDate, double price) {super(name, producedDate, price);}@Overridepublic void accept(Visitor visitor) {//accept实现方法中调用访问者并将自己 "this" 传回。this是一个明确的身份,不存在任何泛型visitor.visit(this);}
}//酒水与水果类同样实现Acceptable接口,重写accept方法

测试

public class Client {public static void main(String[] args) {//        //德芙巧克力,生产日期2002-5-1 ,原价 10元Candy candy = new Candy("德芙巧克力",LocalDate.of(2022,5,1),10.0);Visitor visitor = new DiscountVisitor(LocalDate.of(2022,10,11));visitor.visit(candy);//模拟添加多个商品的操作List<Acceptable> products = Arrays.asList(new Candy("金丝猴奶糖",LocalDate.of(2022,6,10),10.00),new Wine("衡水老白干",LocalDate.of(2020,6,10),100.00),new Fruit("草莓",LocalDate.of(2022,10,12),50.00,1));Visitor visitor = new DiscountVisitor(LocalDate.of(2022,10,17));for (Acceptable product : products) {product.accept(visitor);}}
}

4 访问者模式总结

1) 访问者模式优点:

  • 扩展性好

    在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能。

  • 复用性好

    通过访问者来定义整个对象结构通用的功能,从而提高复用程度。

  • 分离无关行为

    通过访问者来分离无关的行为,把相关的行为封装在一起,构成一个访问者,这样每一个访问者的功能都比较单一。

2) 访问者模式缺点:

  • 对象结构变化很困难

    在访问者模式中,每增加一个新的元素类,都要在每一个具体访问者类中增加相应的具体操作,这违背了“开闭原则”。

  • 违反了依赖倒置原则

    访问者模式依赖了具体类,而没有依赖抽象类。

3) 使用场景

  • 当对象的数据结构相对稳定,而操作却经常变化的时候。 比如,上面例子中路由器本身的内部构造(也就是数据结构)不会怎么变化,但是在不同操作系统下的操作可能会经常变化,比如,发送数据、接收数据等。

  • 需要将数据结构与不常用的操作进行分离的时候。 比如,扫描文件内容这个动作通常不是文件常用的操作,但是对于文件夹和文件来说,和数据结构本身没有太大关系(树形结构的遍历操作),扫描是一个额外的动作,如果给每个文件都添加一个扫描操作会太过于重复,这时采用访问者模式是非常合适的,能够很好分离文件自身的遍历操作和外部的扫描操作。

  • 需要在运行时动态决定使用哪些对象和方法的时候。 比如,对于监控系统来说,很多时候需要监控运行时的程序状态,但大多数时候又无法预知对象编译时的状态和参数,这时使用访问者模式就可以动态增加监控行为。

这篇关于第二十一章 访问者模式的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1069710

相关文章

在JS中的设计模式的单例模式、策略模式、代理模式、原型模式浅讲

1. 单例模式(Singleton Pattern) 确保一个类只有一个实例,并提供一个全局访问点。 示例代码: class Singleton {constructor() {if (Singleton.instance) {return Singleton.instance;}Singleton.instance = this;this.data = [];}addData(value)

模版方法模式template method

学习笔记,原文链接 https://refactoringguru.cn/design-patterns/template-method 超类中定义了一个算法的框架, 允许子类在不修改结构的情况下重写算法的特定步骤。 上层接口有默认实现的方法和子类需要自己实现的方法

【iOS】MVC模式

MVC模式 MVC模式MVC模式demo MVC模式 MVC模式全称为model(模型)view(视图)controller(控制器),他分为三个不同的层分别负责不同的职责。 View:该层用于存放视图,该层中我们可以对页面及控件进行布局。Model:模型一般都拥有很好的可复用性,在该层中,我们可以统一管理一些数据。Controlller:该层充当一个CPU的功能,即该应用程序

迭代器模式iterator

学习笔记,原文链接 https://refactoringguru.cn/design-patterns/iterator 不暴露集合底层表现形式 (列表、 栈和树等) 的情况下遍历集合中所有的元素

《x86汇编语言:从实模式到保护模式》视频来了

《x86汇编语言:从实模式到保护模式》视频来了 很多朋友留言,说我的专栏《x86汇编语言:从实模式到保护模式》写得很详细,还有的朋友希望我能写得更细,最好是覆盖全书的所有章节。 毕竟我不是作者,只有作者的解读才是最权威的。 当初我学习这本书的时候,只能靠自己摸索,网上搜不到什么好资源。 如果你正在学这本书或者汇编语言,那你有福气了。 本书作者李忠老师,以此书为蓝本,录制了全套视频。 试

利用命令模式构建高效的手游后端架构

在现代手游开发中,后端架构的设计对于支持高并发、快速迭代和复杂游戏逻辑至关重要。命令模式作为一种行为设计模式,可以有效地解耦请求的发起者与接收者,提升系统的可维护性和扩展性。本文将深入探讨如何利用命令模式构建一个强大且灵活的手游后端架构。 1. 命令模式的概念与优势 命令模式通过将请求封装为对象,使得请求的发起者和接收者之间的耦合度降低。这种模式的主要优势包括: 解耦请求发起者与处理者

springboot实战学习(1)(开发模式与环境)

目录 一、实战学习的引言 (1)前后端的大致学习模块 (2)后端 (3)前端 二、开发模式 一、实战学习的引言 (1)前后端的大致学习模块 (2)后端 Validation:做参数校验Mybatis:做数据库的操作Redis:做缓存Junit:单元测试项目部署:springboot项目部署相关的知识 (3)前端 Vite:Vue项目的脚手架Router:路由Pina:状态管理Eleme

状态模式state

学习笔记,原文链接 https://refactoringguru.cn/design-patterns/state 在一个对象的内部状态变化时改变其行为, 使其看上去就像改变了自身所属的类一样。 在状态模式中,player.getState()获取的是player的当前状态,通常是一个实现了状态接口的对象。 onPlay()是状态模式中定义的一个方法,不同状态下(例如“正在播放”、“暂停

软件架构模式:5 分钟阅读

原文: https://orkhanscience.medium.com/software-architecture-patterns-5-mins-read-e9e3c8eb47d2 软件架构模式:5 分钟阅读 当有人潜入软件工程世界时,有一天他需要学习软件架构模式的基础知识。当我刚接触编码时,我不知道从哪里获得简要介绍现有架构模式的资源,这样它就不会太详细和混乱,而是非常抽象和易

使用Spring Boot集成Spring Data JPA和单例模式构建库存管理系统

引言 在企业级应用开发中,数据库操作是非常重要的一环。Spring Data JPA提供了一种简化的方式来进行数据库交互,它使得开发者无需编写复杂的JPA代码就可以完成常见的CRUD操作。此外,设计模式如单例模式可以帮助我们更好地管理和控制对象的创建过程,从而提高系统的性能和可维护性。本文将展示如何结合Spring Boot、Spring Data JPA以及单例模式来构建一个基本的库存管理系统