Java访问者模式操作元素结构
模式概述
访问者模式(Visitor Pattern)是一种行为设计模式,它允许你在不修改已有元素类的情况下,为这些元素类添加新的操作。

核心概念
主要角色:
- Visitor(访问者):声明访问各种元素的操作接口
- ConcreteVisitor(具体访问者):实现访问者接口,定义具体操作
- Element(元素):声明接受访问者的接口
- ConcreteElement(具体元素):实现接受访问者的方法
- ObjectStructure(对象结构):管理元素集合,提供遍历机制
代码示例
1 元素接口和具体元素
// 元素接口
interface Element {
void accept(Visitor visitor);
}
// 具体元素:书
class Book implements Element {
private String title;
private double price;
public Book(String title, double price) {
this.title = title;
this.price = price;
}
public String getTitle() {
return title;
}
public double getPrice() {
return price;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
// 具体元素:电子产品
class Electronics implements Element {
private String name;
private double price;
private double weight;
public Electronics(String name, double price, double weight) {
this.name = name;
this.price = price;
this.weight = weight;
}
public String getName() {
return name;
}
public double getPrice() {
return price;
}
public double getWeight() {
return weight;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
2 访问者接口和具体访问者
// 访问者接口
interface Visitor {
void visit(Book book);
void visit(Electronics electronics);
}
// 具体访问者:价格计算器
class PriceCalculator implements Visitor {
private double totalPrice = 0;
@Override
public void visit(Book book) {
totalPrice += book.getPrice();
System.out.println("添加书籍: " + book.getTitle() +
", 价格: " + book.getPrice());
}
@Override
public void visit(Electronics electronics) {
totalPrice += electronics.getPrice();
System.out.println("添加电子产品: " + electronics.getName() +
", 价格: " + electronics.getPrice());
}
public double getTotalPrice() {
return totalPrice;
}
}
// 具体访问者:折扣计算器
class DiscountCalculator implements Visitor {
private double totalDiscount = 0;
@Override
public void visit(Book book) {
// 书籍打8折
double discount = book.getPrice() * 0.2;
totalDiscount += discount;
System.out.println("书籍优惠: " + book.getTitle() +
", 优惠金额: " + discount);
}
@Override
public void visit(Electronics electronics) {
// 电子产品打9折
double discount = electronics.getPrice() * 0.1;
totalDiscount += discount;
System.out.println("电子产品优惠: " + electronics.getName() +
", 优惠金额: " + discount);
}
public double getTotalDiscount() {
return totalDiscount;
}
}
// 具体访问者:商品清单打印机
class InventoryPrinter implements Visitor {
@Override
public void visit(Book book) {
System.out.println("书籍 - 书名: " + book.getTitle() +
", 价格: " + book.getPrice());
}
@Override
public void visit(Electronics electronics) {
System.out.println("电子产品 - 名称: " + electronics.getName() +
", 价格: " + electronics.getPrice() +
", 重量: " + electronics.getWeight() + "kg");
}
}
3 对象结构
import java.util.ArrayList;
import java.util.List;
class ShoppingCart {
private List<Element> items = new ArrayList<>();
public void addItem(Element item) {
items.add(item);
}
public void accept(Visitor visitor) {
for (Element item : items) {
item.accept(visitor);
}
}
}
4 客户端代码
public class VisitorPatternDemo {
public static void main(String[] args) {
// 创建购物车
ShoppingCart cart = new ShoppingCart();
// 添加商品
cart.addItem(new Book("Java编程思想", 89.0));
cart.addItem(new Book("设计模式", 68.0));
cart.addItem(new Electronics("笔记本电脑", 5999.0, 2.5));
cart.addItem(new Electronics("智能手机", 3999.0, 0.2));
System.out.println("=== 商品清单 ===");
cart.accept(new InventoryPrinter());
System.out.println("\n=== 价格计算 ===");
PriceCalculator priceCalc = new PriceCalculator();
cart.accept(priceCalc);
System.out.println("总价: " + priceCalc.getTotalPrice());
System.out.println("\n=== 折扣计算 ===");
DiscountCalculator discountCalc = new DiscountCalculator();
cart.accept(discountCalc);
System.out.println("总优惠: " + discountCalc.getTotalDiscount());
System.out.println("最终价格: " +
(priceCalc.getTotalPrice() - discountCalc.getTotalDiscount()));
}
}
运行结果
=== 商品清单 ===
书籍 - 书名: Java编程思想, 价格: 89.0
书籍 - 书名: 设计模式, 价格: 68.0
电子产品 - 名称: 笔记本电脑, 价格: 5999.0, 重量: 2.5kg
电子产品 - 名称: 智能手机, 价格: 3999.0, 重量: 0.2kg
=== 价格计算 ===
添加书籍: Java编程思想, 价格: 89.0
添加书籍: 设计模式, 价格: 68.0
添加笔记本电脑: 5999.0
添加智能手机: 3999.0
总价: 10155.0
=== 折扣计算 ===
书籍优惠: Java编程思想, 优惠金额: 17.8
书籍优惠: 设计模式, 优惠金额: 13.6
电子产品优惠: 笔记本电脑, 优惠金额: 599.9
电子产品优惠: 智能手机, 优惠金额: 399.9
总优惠: 1031.2
最终价格: 9123.8
优缺点分析
优点:
- 开闭原则:易于添加新的操作,无需修改元素类
- 单一职责:将相关操作集中到访问者中
- 数据聚合:访问者可以积累状态(如计算总和)
缺点:
- 违反依赖倒置:访问者依赖具体元素类
- 扩展困难:添加新元素类型需要修改所有访问者
- 破坏封装:访问者需要访问元素内部状态
适用场景
- 对象结构稳定,但需要经常添加新操作
- 需要对不同元素执行不同的操作
- 元素类不需要频繁变化,但操作需要经常变化
最佳实践
// 在实际开发中,可以使用更灵活的设计
interface ImprovedVisitor<T> {
T visit(Book book);
T visit(Electronics electronics);
}
// 或者使用泛型访问者
abstract class GenericVisitor<T> {
protected abstract T visitBook(Book book);
protected abstract T visitElectronics(Electronics electronics);
}
访问者模式在编译器和语法分析器等领域有广泛应用,它能够很好地处理复杂的对象结构,并将操作与数据结构分离。