定义
访问者模式(Visitor Pattern)是一种行为型设计模式,其核心思想是将数据结构与数据操作解耦,允许在不修改现有对象结构的前提下定义作用于对象元素的新操作。
访问者模式有以下核心要点:
1)数据结构稳定,被访问的对象结构(元素类)相对固定,不频繁变动。
2)操作可扩展,新增操作只需添加新的访问者类,无需修改元素类代码,符合开闭原则。
3)双分派机制,通过accept(Visitor)和visit(Element)的两次动态绑定,根据元素类型调用对应操作。
结构
适用场景
访问者模式适用于以下场景:
1)对象结构稳定但需频繁新增操作,如编译器语法树分析(类型检查、代码优化等操作)。
2)避免元素类被污染,当元素类不宜直接包含某些操作时(如电商商品类不宜包含打折、导出等业务逻辑)。
3)统一处理复合结构,对集合、树形结构中的异构元素执行统一操作(如文档导出、统计计算)。
使用示例
电商订单价格计算。假设订单包含不同类型商品(普通商品/折扣商品),需支持多种计算规则(含税价计算、满减计算)。
定义抽象Element
/**
* 定义商品元素接口(抽象Element角色)
*/
public interface OrderItem {
void accept(PriceVisitor visitor);
double getBasePrice(); // 所有商品必须提供基础价格
}
定义具体Element
/**
* 具体商品类(普通商品)
*/
public class RegularItem implements OrderItem {
private String name;
private double price;
public RegularItem(String name, double price) {
this.name = name;
this.price = price;
}
@Override
public void accept(PriceVisitor visitor) {
visitor.visit(this); // 关键:将自身作为参数传递
}
@Override
public double getBasePrice() {
return price;
}
// 普通商品特有方法
public String getName() {
return name;
}
}
/**
* 具体商品类(折扣商品)
*/
public class DiscountItem implements OrderItem {
private String name;
private double price;
private double discountRate; // 折扣率
public DiscountItem(String name, double price, double discountRate) {
this.name = name;
this.price = price;
this.discountRate = discountRate;
}
@Override
public void accept(PriceVisitor visitor) {
visitor.visit(this); // 关键:将自身作为参数传递
}
@Override
public double getBasePrice() {
return price * discountRate;
}
// 折扣商品特有方法
public double getDiscountRate() {
return discountRate;
}
public String getName() {
return name;
}
}
定义抽象Visitor
/**
* 访问者接口(抽象Visitor角色)
*/
public interface PriceVisitor {
void visit(RegularItem item);
void visit(DiscountItem item);
}
定义具体Visitor
/**
* 具体访问者:满减计算
*/
public class PromotionVisitor implements PriceVisitor {
private final double threshold; // 满减阈值
private final double reduction; // 减免金额
public PromotionVisitor(double threshold, double reduction) {
this.threshold = threshold;
this.reduction = reduction;
}
@Override
public void visit(RegularItem item) {
double finalPrice = item.getBasePrice();
if (finalPrice >= threshold) {
finalPrice -= reduction;
System.out.printf("普通商品[%s] 满减后价格: %.2f\n",
item.getName(), finalPrice);
}
}
@Override
public void visit(DiscountItem item) {
// 折扣商品不参与满减
System.out.println("折扣商品[" + item.getName() + "]不参与满减活动");
}
}
/**
* 具体访问者:含税价格计算
*/
public class TaxPriceVisitor implements PriceVisitor {
private double totalTax = 0;
private final double taxRate;
public TaxPriceVisitor(double taxRate) {
this.taxRate = taxRate;
}
@Override
public void visit(RegularItem item) {
double tax = item.getBasePrice() * taxRate;
totalTax += tax;
System.out.printf("普通商品[%s] 含税价: %.2f (税额: %.2f)\n",
item.getName(), item.getBasePrice() + tax, tax);
}
@Override
public void visit(DiscountItem item) {
double tax = item.getBasePrice() * taxRate * 0.5; // 折扣商品税额减半
totalTax += tax;
System.out.printf("折扣商品[%s]含税价: %.2f (折扣率: %.1f, 税额: %.2f)\n",item.getName(),
item.getBasePrice() + tax, item.getDiscountRate(), tax);
}
public double getTotalTax() {
return totalTax;
}
}
定义ObjectStructure角色
/**
* 对象结构(订单)(ObjectStructure角色)
*/
public class Order {
private List<OrderItem> items = new ArrayList<>();
public void addItem(OrderItem item) {
items.add(item);
}
public void accept(PriceVisitor visitor) {
for (OrderItem item : items) {
item.accept(visitor); // 触发双分派
}
}
}
测试
public class Client {
public static void main(String[] args) {
Order order = new Order();
order.addItem(new RegularItem("iPhone", 5999));
order.addItem(new DiscountItem("运动鞋", 399, 0.8));
// 计算含税价
TaxPriceVisitor taxVisitor = new TaxPriceVisitor(0.13);
order.accept(taxVisitor);
System.out.println("总税额: " + taxVisitor.getTotalTax());
// 计算满减
PromotionVisitor promoVisitor = new PromotionVisitor(5000, 300);
order.accept(promoVisitor);
}
}