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

访问者模式深度解析与实战案例

一、模式定义

访问者模式(Visitor Pattern) 是一种行为设计模式,允许在不修改对象结构的前提下,为对象结构中的元素定义新的操作。核心思想是 算法与对象结构分离,实现 双重分派(Double Dispatch) 机制。

二、核心组件

组件作用
Visitor声明访问具体元素的visit方法
ConcreteVisitor实现具体的访问逻辑
Element定义accept方法接受访问者
ConcreteElement具体元素,实现accept方法
ObjectStructure包含元素的集合,提供遍历接口

三、模式优势

开闭原则:新增访问逻辑无需修改元素类
聚合操作:相关操作集中到同一访问者
复杂操作解耦:分离核心业务与辅助功能
跨类层级操作:处理不同类型对象的统一接口

四、真实场景案例:电商平台商品分析系统

场景需求
商品类型包含:图书、电子产品、服装
需要实现:
价格计算(含折扣策略)
库存预警检查
商品信息导出(XML/JSON)
未来可能新增商品类型和分析功能

五、Java实现代码

1. 元素接口与实现

// 商品元素接口
public interface ProductElement {void accept(ProductVisitor visitor);
}// 具体商品类
class Book implements ProductElement {private String title;private double price;private int stock;private double discount; // 专属折扣public Book(String title, double price, int stock, double discount) {this.title = title;this.price = price;this.stock = stock;this.discount = discount;}@Overridepublic void accept(ProductVisitor visitor) {visitor.visit(this);}// Getters...
}class Electronics implements ProductElement {private String model;private double price;private int stock;private String warranty; // 保修期public Electronics(String model, double price, int stock, String warranty) {this.model = model;this.price = price;this.stock = stock;this.warranty = warranty;}@Overridepublic void accept(ProductVisitor visitor) {visitor.visit(this);}// Getters...
}class Apparel implements ProductElement {private String style;private double price;private int stock;private String sizeChart; // 尺码表public Apparel(String style, double price, int stock, String sizeChart) {this.style = style;this.price = price;this.stock = stock;this.sizeChart = sizeChart;}@Overridepublic void accept(ProductVisitor visitor) {visitor.visit(this);}// Getters...
}

2. 访问者接口与实现

// 访问者接口
public interface ProductVisitor {void visit(Book book);void visit(Electronics electronics);void visit(Apparel apparel);
}// 价格计算访问者
class PriceCalculator implements ProductVisitor {private double total = 0;@Overridepublic void visit(Book book) {total += book.getPrice() * book.getDiscount();}@Overridepublic void visit(Electronics electronics) {// 电子产品无折扣total += electronics.getPrice();}@Overridepublic void visit(Apparel apparel) {// 服装类满200减50double price = apparel.getPrice();total += price >= 200 ? price - 50 : price;}public double getTotal() {return total;}
}// 库存检查访问者
class StockChecker implements ProductVisitor {private List<String> warnings = new ArrayList<>();@Overridepublic void visit(Book book) {if (book.getStock() < 10) {warnings.add("图书缺货: " + book.getTitle());}}@Overridepublic void visit(Electronics electronics) {if (electronics.getStock() < 5) {warnings.add("电子产品缺货: " + electronics.getModel());}}@Overridepublic void visit(Apparel apparel) {if (apparel.getStock() < 20) {warnings.add("服装缺货: " + apparel.getStyle());}}public void printWarnings() {warnings.forEach(System.out::println);}
}// 数据导出访问者(JSON格式)
class JsonExporter implements ProductVisitor {private StringBuilder json = new StringBuilder("[\n");@Overridepublic void visit(Book book) {json.append(String.format("""{"type": "Book","title": "%s","price": %.2f,"stock": %d},""", book.getTitle(), book.getPrice(), book.getStock()));}@Overridepublic void visit(Electronics electronics) {json.append(String.format("""{"type": "Electronics","model": "%s","warranty": "%s","price": %.2f},""", electronics.getModel(), electronics.getWarranty(), electronics.getPrice()));}@Overridepublic void visit(Apparel apparel) {json.append(String.format("""{"type": "Apparel","style": "%s","sizes": "%s","price": %.2f},""", apparel.getStyle(), apparel.getSizeChart(), apparel.getPrice()));}public String getJson() {// 去除最后一个逗号if (json.charAt(json.length()-1) == ',') {json.deleteCharAt(json.length()-1);}return json.append("\n]").toString();}
}

3. 对象结构管理

public class ProductInventory {private List<ProductElement> products = new ArrayList<>();public void addProduct(ProductElement product) {products.add(product);}public void applyVisitor(ProductVisitor visitor) {products.forEach(p -> p.accept(visitor));}
}

4. 客户端使用

public class ECommerceSystem {public static void main(String[] args) {// 初始化商品库ProductInventory inventory = new ProductInventory();inventory.addProduct(new Book("设计模式", 89.99, 8, 0.8));inventory.addProduct(new Electronics("智能手机", 2999.0, 4, "2年保修"));inventory.addProduct(new Apparel("男士衬衫", 199.0, 25, "S/M/L/XL"));// 执行价格计算PriceCalculator priceCalc = new PriceCalculator();inventory.applyVisitor(priceCalc);System.out.printf("总金额: ¥%.2f%n", priceCalc.getTotal());// 执行库存检查StockChecker stockCheck = new StockChecker();inventory.applyVisitor(stockCheck);System.out.println("\n库存预警:");stockCheck.printWarnings();// 执行数据导出JsonExporter exporter = new JsonExporter();inventory.applyVisitor(exporter);System.out.println("\n商品JSON:");System.out.println(exporter.getJson());}
}

六、运行结果

总金额: ¥2979.19库存预警:
图书缺货: 设计模式
电子产品缺货: 智能手机商品JSON:
[{"type": "Book","title": "设计模式","price": 89.99,"stock": 8},{"type": "Electronics","model": "智能手机","warranty": "2年保修","price": 2999.00},{"type": "Apparel","style": "男士衬衫","sizes": "S/M/L/XL","price": 199.00}
]

七、模式变体与优化

1. 动态访问者

// 使用反射处理未知类型
public class UniversalVisitor implements ProductVisitor {@Overridepublic void visit(Object element) {try {Method visitMethod = this.getClass().getMethod("visit", element.getClass());visitMethod.invoke(this, element);} catch (Exception e) {// 默认处理}}
}

2. 组合访问者

// 组合多个访问者操作
public class BatchVisitor implements ProductVisitor {private List<ProductVisitor> visitors = new ArrayList<>();public BatchVisitor add(ProductVisitor visitor) {visitors.add(visitor);return this;}@Overridepublic void visit(Book book) {visitors.forEach(v -> v.visit(book));}// 其他visit方法同理...
}

八、行业应用场景

场景具体应用优势体现
编译器构建语法树分析(类型检查/代码生成)分离语法结构与分析逻辑
文档处理XML/JSON节点处理支持多种格式转换
UI框架GUI组件树遍历(渲染/布局)统一处理不同组件类型
医疗系统检查报告分析(不同科室视角)扩展新分析维度不影响核心数据
游戏引擎场景图遍历(碰撞检测/渲染)优化性能关键路径

九、最佳实践建议

访问者生命周期管理

// 使用工厂模式创建访问者
public class VisitorFactory {public static ProductVisitor createPriceCalculator() {return new PriceCalculator();}public static ProductVisitor createStockChecker() {return new StockChecker();}
}

状态管理策略

// 上下文传递参数
public interface ContextAwareVisitor extends ProductVisitor {void setContext(AnalysisContext context);
}class PriceCalculator implements ContextAwareVisitor {private AnalysisContext context;@Overridepublic void setContext(AnalysisContext context) {this.context = context;}
}

性能优化

// 缓存访问结果
public class CachedVisitor implements ProductVisitor {private Map<ProductElement, Object> cache = new WeakHashMap<>();@Overridepublic void visit(Book book) {if (!cache.containsKey(book)) {// 执行计算并缓存结果}}
}

十、与相似模式对比

模式核心差异适用场景
访问者集中处理异构对象操作需要为对象结构添加多种新功能
迭代器专注集合遍历需要统一遍历接口
装饰器动态添加对象功能需要透明扩展对象功能
策略封装可互换算法需要动态切换单一算法

通过这个电商商品分析案例,可以看到访问者模式如何 有效管理对复杂对象结构的多种操作。该模式特别适合以下场景:
需要对复杂对象结构(如组合结构)进行多种不相关操作
操作需要访问不同类型对象的私有状态
操作可能频繁变化,而对象结构相对稳定
实际使用时需权衡访问者模式的灵活性带来的复杂性,建议在操作类型明显多于元素类型时采用本模式。

一句话总结

访问者模式是在不修改被访问者类代码的前提下,通过访问者动态地为被访问者添加新的操作或功能。(多个对象)

相关文章:

  • AI日报 - 2025年4月18日
  • 【KWDB 创作者计划】深度实操体验 KWDB 2.2.0:从安装到实战的全流程解析以及实操体验
  • 线程同步——互斥锁
  • Elastic 9.0/8.18:BBQ、EDOT 和 LLM 可观察性、攻击发现、自动导入以及 ES|QL JOIN
  • maptalks矩形绘制结束后,获取最大经度最大纬度,最小经度最小纬度,从左上角开始依次获取并展示坐标
  • HarmonyOS-ArkUI: 自定义组件冻结功能@ComonentV2 freezeWhenInactive属性
  • 【FAQ】HarmonyOS SDK 闭源开放能力 —Health Service Kit
  • 【cocos creator 3.x】速通3d模型导入, 模型创建,阴影,材质使用,模型贴图绑定
  • Electricity Market Optimization 探索系列(V)
  • Ubuntu 部署 DeepSeek
  • 条款05:了解C++默默编写并调用哪些函数
  • 【工具】视频翻译、配音、语音克隆于一体的一站式视频多语言转换工具~
  • 【Netty篇】Handler Pipeline 详解
  • linux多线(进)程编程——(8)多进程的冲突问题
  • 【Contiki】Contiki源码目录结构
  • Android启动初始化init.rc详解
  • PotPlayer在AMD 25.3.1以上时出现画面不动问题
  • FreeBSD系统使用 ZFS 添加交换空间swap
  • 【C++】特化妙技与分文件编写 “雷区”
  • 前端渲染pdf文件解决方案
  • 智利观众也喜欢上海的《好东西》
  • 巴基斯坦外长:近期军事回应是自卫措施
  • 巴基斯坦首都及邻近城市听到巨大爆炸声
  • 招商蛇口:今年前4个月销售额约498.34亿元
  • 国家税务总局泰安市税务局:山东泰山啤酒公司欠税超536万元
  • 范志毅跨界归来做青训,探索中国足球人才培养新模式