23种设计模式之【访问者模式】-核心原理与 Java 实践
访问者模式(Visitor Pattern)
访问者模式是 23 种设计模式中的一种行为型模式,它的核心思想是将数据结构与数据操作分离,使得可以在不修改原有数据结构的前提下,为数据结构中的元素添加新的操作。这种模式通过一个 “访问者” 对象封装对元素的操作,从而实现操作的灵活扩展。
核心原理
抽象访问者(Visitor):
声明对每一种具体元素的访问操作(visit(ConcreteElementA)、visit(ConcreteElementB)等)
每个方法对应一种元素类型的操作
具体访问者(ConcreteVisitor):
实现抽象访问者声明的所有方法
定义对具体元素的具体操作逻辑
抽象元素(Element):
声明一个接受访问者的方法(accept(Visitor visitor))
该方法通常会调用访问者的对应方法(visitor.visit(this))
具体元素(ConcreteElement):
实现抽象元素的accept()方法
表示数据结构中的具体元素
对象结构(ObjectStructure):
存储元素集合,提供遍历元素的方法
可以是集合、列表、树等数据结构
访问者模式的核心是 **“双分派” 机制 **:元素的accept()方法接收访问者,然后调用访问者的visit()方法并传入自身,从而确定具体的操作逻辑。
Java 实践示例
下面以 “商品购物车” 为例实现访问者模式:
购物车中有不同类型的商品(书籍、电子产品)
需要对商品执行不同操作(计算价格、生成订单信息)
package com.example.demo;
import java.util.ArrayList;
import java.util.List;
public class VisitorPattern {
public static void main(String[] args) {
// 创建商品
Element book = new Book("设计模式", "Gamma等", 89.0);
Element phone = new Electronic("智能手机", 4999.0, 2);
Element laptop = new Electronic("笔记本电脑", 7999.0, 3);
// 创建购物车并添加商品
ShoppingCart cart = new ShoppingCart();
cart.addProduct(book);
cart.addProduct(phone);
cart.addProduct(laptop);
// 使用价格计算器访问购物车
PriceCalculator priceVisitor = new PriceCalculator();
cart.accept(priceVisitor);
System.out.println("总价:" + priceVisitor.getTotalPrice());
System.out.println("\n--- 订单信息 ---");
// 使用订单生成器访问购物车
OrderGenerator orderVisitor = new OrderGenerator();
cart.accept(orderVisitor);
System.out.println(orderVisitor.getOrderInfo());
}
// 抽象访问者接口
public interface Visitor {
// 访问书籍商品
void visit(Book book);
// 访问电子产品
void visit(Electronic electronic);
}
// 抽象元素接口
public interface Element {
// 接受访问者访问
void accept(Visitor visitor);
}
// 具体元素:电子产品
public static class Electronic implements Element {
private String name;
private double price;
private int warrantyPeriod; // 保修年限
public Electronic(String name, double price, int warrantyPeriod) {
this.name = name;
this.price = price;
this.warrantyPeriod = warrantyPeriod;
}
// getter方法
public String getName() { return name; }
public double getPrice() { return price; }
public int getWarrantyPeriod() { return warrantyPeriod; }
@Override
public void accept(Visitor visitor) {
// 调用访问者的对应方法
visitor.visit(this);
}
}
// 具体元素:书籍
public static class Book implements Element {
private String name;
private String author;
private double price;
public Book(String name, String author, double price) {
this.name = name;
this.author = author;
this.price = price;
}
// getter方法
public String getName() { return name; }
public String getAuthor() { return author; }
public double getPrice() { return price; }
@Override
public void accept(Visitor visitor) {
// 调用访问者的对应方法
visitor.visit(this);
}
}
//对象结构
public static class ShoppingCart {
private List<Element> products = new ArrayList<>();
// 添加商品
public void addProduct(Element product) {
products.add(product);
}
// 移除商品
public void removeProduct(Element product) {
products.remove(product);
}
// 让访问者访问所有商品
public void accept(Visitor visitor) {
for (Element product : products) {
product.accept(visitor);
}
}
}
// 具体访问者:计算价格
public static class PriceCalculator implements Visitor {
private double totalPrice;
@Override
public void visit(Book book) {
// 书籍有折扣
double price = book.getPrice() * 0.9;
totalPrice += price;
System.out.println("书籍《" + book.getName() + "》折后价:" + price);
}
@Override
public void visit(Electronic electronic) {
// 电子产品有税费
double price = electronic.getPrice() * 1.1;
totalPrice += price;
System.out.println("电子产品" + electronic.getName() + "含税价:" + price);
}
public double getTotalPrice() {
return totalPrice;
}
}
// 具体访问者:生成订单信息
public static class OrderGenerator implements Visitor {
private StringBuilder orderInfo = new StringBuilder();
@Override
public void visit(Book book) {
orderInfo.append("书籍:").append(book.getName())
.append(",作者:").append(book.getAuthor()).append("\n");
}
@Override
public void visit(Electronic electronic) {
orderInfo.append("电子产品:").append(electronic.getName())
.append(",保修:").append(electronic.getWarrantyPeriod()).append("年\n");
}
public String getOrderInfo() {
return orderInfo.toString();
}
}
}
代码解析
访问者相关:
Visitor接口定义了对两种商品的访问方法
PriceCalculator实现了计算价格的逻辑(书籍打折、电子产品含税)
OrderGenerator实现了生成订单信息的逻辑
元素相关:
Element接口声明了accept()方法
Book和Electronic实现了accept(),并调用访问者对应的visit()方法
对象结构:
ShoppingCart作为对象结构,存储商品集合并提供遍历接口
运行结果:
书籍《设计模式》折后价:80.1
电子产品智能手机含税价:5498.9
电子产品笔记本电脑含税价:8798.9
总价:14377.9
--- 订单信息 ---
书籍:设计模式,作者:Gamma等
电子产品:智能手机,保修:2年
电子产品:笔记本电脑,保修:3年
访问者模式的应用场景
访问者模式适用于数据结构稳定,但需要频繁添加新操作的场景,典型应用包括:
数据结构与操作分离:
当需要对集合中的不同元素执行多种不相关操作(如统计、过滤、转换)时
例:对文档中的文本、图片、表格等元素执行 “导出 PDF”、“字数统计”、“格式检查” 等操作
复杂对象结构的操作:
当对象结构包含多种类型元素,且需要统一处理这些元素时
例:AST(抽象语法树)的遍历,对不同语法节点(表达式、语句、变量)执行分析、优化、生成代码等操作
报表生成系统:
对相同的数据源生成不同类型报表(表格、图表、统计数据)
例:销售数据可被 “月度报表访问者”、“区域对比访问者”、“利润分析访问者” 处理
编译器设计:
编译器的语义分析、中间代码生成、优化等阶段,可通过访问者模式处理语法树节点
维护遗留系统:
当数据结构难以修改(如第三方库、历史代码),但需要添加新功能时,访问者模式可避免修改原有代码
访问者模式的优缺点
优点:
符合开闭原则,新增操作只需添加新的访问者,无需修改元素类
集中管理多种操作,避免操作逻辑分散在各个元素类中
便于实现对不同元素的差异化处理
缺点:
增加新元素类型时,需修改所有访问者接口和实现类,违反开闭原则
元素类需要暴露内部状态给访问者,可能破坏封装性
依赖关系复杂,理解和维护成本较高
访问者模式适合在元素类型稳定,但操作多变的场景中使用,是 “单一职责原则” 和 “开闭原则” 在特定场景下的平衡选择。
更多推荐



所有评论(0)