当前位置: 首页 > news >正文

Java行为型模式---访问者模式

访问者模式基础概念

访问者模式(Visitor Pattern)是一种行为型设计模式,其核心思想是将对象操作的定义与对象本身分离,通过一个外部的访问者对象来实现对不同类型元素的操作。这种模式允许在不改变元素类的前提下,定义作用于这些元素的新操作,从而满足开闭原则(对扩展开放,对修改关闭)。

访问者模式的核心组件

  1. 访问者接口(Visitor) - 定义对每个具体元素的访问操作,每个操作对应一个具体元素类型。
  2. 具体访问者(ConcreteVisitor) - 实现访问者接口,为每个元素类型提供具体的操作实现。
  3. 元素接口(Element) - 定义接受访问者的接口,通常包含accept(Visitor visitor)方法。
  4. 具体元素(ConcreteElement) - 实现元素接口,通过调用访问者的相应方法来接受访问。
  5. 对象结构(ObjectStructure) - 管理元素集合,提供遍历元素的方式,允许访问者访问其中的元素。

访问者模式的实现

下面通过一个简单的电商系统示例展示访问者模式的实现:

// 1. 访问者接口
interface Visitor {void visit(Book book);void visit(Electronics electronics);void visit(Food food);
}// 2. 具体访问者 - 计算价格(含税)
class TaxVisitor implements Visitor {@Overridepublic void visit(Book book) {double tax = book.getPrice() * 0.05;  // 书籍税率5%System.out.printf("Book: %s, Price: $%.2f, Tax: $%.2f%n", book.getName(), book.getPrice(), tax);}@Overridepublic void visit(Electronics electronics) {double tax = electronics.getPrice() * 0.15;  // 电子产品税率15%System.out.printf("Electronics: %s, Price: $%.2f, Tax: $%.2f%n", electronics.getName(), electronics.getPrice(), tax);}@Overridepublic void visit(Food food) {double tax = food.getPrice() * 0.08;  // 食品税率8%System.out.printf("Food: %s, Price: $%.2f, Tax: $%.2f%n", food.getName(), food.getPrice(), tax);}
}// 3. 具体访问者 - 计算折扣
class DiscountVisitor implements Visitor {@Overridepublic void visit(Book book) {double discount = book.getPrice() * 0.1;  // 书籍折扣10%System.out.printf("Book: %s, Price: $%.2f, Discount: $%.2f%n", book.getName(), book.getPrice(), discount);}@Overridepublic void visit(Electronics electronics) {double discount = electronics.getPrice() * 0.2;  // 电子产品折扣20%System.out.printf("Electronics: %s, Price: $%.2f, Discount: $%.2f%n", electronics.getName(), electronics.getPrice(), discount);}@Overridepublic void visit(Food food) {double discount = food.getPrice() * 0.05;  // 食品折扣5%System.out.printf("Food: %s, Price: $%.2f, Discount: $%.2f%n", food.getName(), food.getPrice(), discount);}
}// 4. 元素接口
interface Element {void accept(Visitor visitor);
}// 5. 具体元素 - 书籍
class Book implements Element {private String name;private double price;public Book(String name, double price) {this.name = name;this.price = price;}public String getName() {return name;}public double getPrice() {return price;}@Overridepublic void accept(Visitor visitor) {visitor.visit(this);  // 调用访问者的相应方法}
}// 6. 具体元素 - 电子产品
class Electronics implements Element {private String name;private double price;public Electronics(String name, double price) {this.name = name;this.price = price;}public String getName() {return name;}public double getPrice() {return price;}@Overridepublic void accept(Visitor visitor) {visitor.visit(this);}
}// 7. 具体元素 - 食品
class Food implements Element {private String name;private double price;public Food(String name, double price) {this.name = name;this.price = price;}public String getName() {return name;}public double getPrice() {return price;}@Overridepublic void accept(Visitor visitor) {visitor.visit(this);}
}// 8. 对象结构
class ShoppingCart {private List<Element> items = new ArrayList<>();public void addItem(Element item) {items.add(item);}public void removeItem(Element item) {items.remove(item);}public void accept(Visitor visitor) {for (Element item : items) {item.accept(visitor);  // 遍历元素并接受访问}}
}// 9. 客户端代码
public class VisitorPatternClient {public static void main(String[] args) {// 创建购物车并添加商品ShoppingCart cart = new ShoppingCart();cart.addItem(new Book("Java编程思想", 59.99));cart.addItem(new Electronics("笔记本电脑", 1299.99));cart.addItem(new Food("巧克力", 4.99));// 应用税务访问者System.out.println("=== 计算税费 ===");Visitor taxVisitor = new TaxVisitor();cart.accept(taxVisitor);// 应用折扣访问者System.out.println("\n=== 计算折扣 ===");Visitor discountVisitor = new DiscountVisitor();cart.accept(discountVisitor);}
}

访问者模式的应用场景

  1. 对象结构稳定但操作多变 - 当需要对一个稳定的对象结构(如 XML 文档、AST 树)定义多种操作时
  2. 跨类操作 - 需要对不同类型的对象进行集中操作,而不希望在每个类中添加这些操作
  3. 数据结构与操作分离 - 当数据结构和作用于数据结构的操作需要独立变化时
  4. 复杂计算 - 如计算表达式树的值、文档格式化等
  5. 权限控制 - 根据访问者的不同权限执行不同的操作
  6. 报表生成 - 对不同类型的数据生成统一格式的报表

访问者模式的优缺点

优点

  • 符合开闭原则 - 可以在不修改元素类的前提下,新增作用于元素的操作
  • 操作集中 - 将相关操作集中在一个访问者类中,提高内聚性
  • 分离关注点 - 数据结构与作用于数据的操作分离,便于维护
  • 多分派 - 通过双分派(element.accept(visitor)visitor.visit(element))实现动态调度
  • 扩展性好 - 可以轻松添加新的访问者,支持新的操作需求

缺点

  • 违反依赖倒置原则 - 访问者依赖具体元素类,而非抽象接口
  • 破坏封装性 - 访问者可能需要访问元素的内部状态,破坏元素的封装
  • 增加系统复杂度 - 为每个新操作都需要创建一个新的访问者类
  • 对象结构固定 - 若对象结构经常变化(如新增元素类型),需修改所有访问者类
  • 双分派实现复杂 - 在不支持双分派的语言中(如 Java),实现较为繁琐

使用访问者模式的注意事项

  1. 对象结构稳定 - 访问者模式最适合对象结构相对稳定的场景
  2. 操作变化频繁 - 当需要频繁新增操作时,访问者模式是理想选择
  3. 封装与访问的平衡 - 设计元素接口时,需平衡封装性和访问者对内部状态的需求
  4. 访问者接口设计 - 确保访问者接口包含所有需要的操作,避免频繁修改接口
  5. 与迭代器结合 - 对象结构通常使用迭代器遍历元素,可结合迭代器模式实现
  6. 避免滥用 - 对于简单场景或操作变化不多的情况,无需使用访问者模式

总结

访问者模式通过将对象操作的定义与对象本身分离,实现了对不同类型元素的操作扩展,同时保持了元素类的稳定性。它特别适用于需要对稳定对象结构定义多种操作的场景,通过新增访问者类即可轻松添加新操作,符合开闭原则。在实际开发中,访问者模式常用于编译器设计、XML 处理、报表生成等领域。合理使用访问者模式可以提高系统的可扩展性和可维护性,但需要注意控制类的数量和维护访问者与元素之间的关系。

http://www.lryc.cn/news/593416.html

相关文章:

  • Nestjs框架: 理解 RxJS响应式编程的核心概念与实践
  • 如何构建未来的人-AI-环境智能教育生态系统
  • OpenCV 官翻 3 - 特征检测 Feature Detection
  • Vue 3 中导出 Excel 文件
  • 【Web APIs】JavaScript 自定义属性操作 ② ( H5 自定义属性 )
  • 光纤收发器上的指示灯各代表的含义
  • 【ChatOpenAI】常用方法详解
  • 如何设计一个软件项目管理系统:架构设计合集(六)
  • min_25筛学习笔记+牛客多校02E
  • AWS Partner: Sales Accreditation (Business)
  • C++命名空间深度解析:避免命名冲突的终极解决方案
  • Kafka、RabbitMQ 与 RocketMQ 高可靠消息保障方案对比分析
  • 【数据结构初阶】--双向链表(二)
  • 明细列表,明细grid中的默认按钮失效,配置按钮失效
  • windows wsl2-06-docker hello world
  • windows wsl ubuntu 如何安装 open-jdk8
  • rustdesk客户端编译
  • NX二次开发常用函数坐标转化UF_MTX4_csys_to_csys和UF_MTX4_vec3_multipl
  • 【REACT18.x】creat-react-app在添加eslint时报错Environment key “jest/globals“ is unknown
  • 【橘子分布式】gRPC(编程篇-中)
  • Vue3生命周期函数
  • SQL基础操作指南:约束、表设计与复杂查询
  • Oracle RU19.28补丁发布,一键升级稳
  • 在摄像机视图中想像在普通 3D 视口里那样随意移动
  • 计算机网络1.1:计算机网络在信息时代的作用
  • 删除debian xdm自启动ibus的配置项
  • rust实现的快捷补全到剪贴板的实用工具
  • 汽车ECU控制器通信架构
  • 解决问题七大步骤
  • 论文reading学习记录4 - weekly - 视觉端到端开创-LOAM