访问者模式是一种行为型设计模式,其核心思想是:将 “数据结构” 与 “数据操作” 解耦,定义一个 “访问者” 对象,封装对数据结构中元素的操作逻辑;数据结构中的元素提供 “接受访问者” 的接口,允许访问者在不修改元素类的前提下,灵活扩展对元素的操作。
该模式的本质是 “分离算法与数据结构”—— 当需要对同一批元素执行多种不同操作(如统计、打印、过滤)时,无需在元素类中添加大量操作方法,只需新增对应的访问者类,符合 “开闭原则”。
访问者模式的UML如下:

访问者模式的典型应用场景是:
- 数据结构稳定,但操作频繁变化:
- 库存系统:商品类型(电子产品、服装)稳定,但需频繁新增操作(统计库存、打印价格、计算折扣);
- 文档解析:文档元素(标题、段落、图片)稳定,但需新增操作(字数统计、格式检查、内容导出)。
- 需要对同一批元素执行多种不相关操作:
- 报表系统:对 “订单”“用户”“商品” 元素,需同时执行 “数据统计”“报表生成”“异常检测” 三种操作,每种操作对应一个访问者。
- 操作需要累计状态:
- 财务系统:遍历 “收入”“支出” 元素,通过访问者累计总利润、统计各类支出占比。
下面将用Java代码实现访问者模式,以商品库存系统为场景:定义“电子产品”和“服装”两种商品元素,通过“库存统计访问者”(统计总数量、总价值)和“价格打印访问者”(打印商品详情)实现不同操作,最终通过“库存管理类”(对象结构)统一管理商品并触发访问者操作。
1. 抽象访问者(Visitor)
定义访问所有商品元素的接口,为每种具体商品声明对应的访问方法:
1 2 3 4 5 6 7
| public abstract class Visitor { public abstract void visitElectronic(Electronic electronic); public abstract void visitClothing(Clothing clothing); }
|
2. 具体访问者(Concrete Visitor)
实现抽象访问者接口,封装对商品的具体操作逻辑:
(1)库存统计访问者(StockCountVisitor)
统计所有商品的总数量和总价值:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| public class StockCountVisitor extends Visitor { private int totalQuantity = 0; private double totalValue = 0.0;
@Override public void visitElectronic(Electronic electronic) { int quantity = electronic.getQuantity(); double value = electronic.getPrice() * quantity; totalQuantity += quantity; totalValue += value; System.out.println("[统计] 电子产品《" + electronic.getName() + "》:" + quantity + "件,价值" + value + "元"); }
@Override public void visitClothing(Clothing clothing) { int quantity = clothing.getQuantity(); double value = clothing.getPrice() * quantity; totalQuantity += quantity; totalValue += value; System.out.println("[统计] 服装《" + clothing.getName() + "》:" + quantity + "件,价值" + value + "元"); }
public void showTotal() { System.out.println("=== 库存统计结果 ==="); System.out.println("总商品数量:" + totalQuantity + "件"); System.out.println("总商品价值:" + totalValue + "元"); } }
|
(2)价格打印访问者(PricePrintVisitor)
打印所有商品的名称和单价:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class PricePrintVisitor extends Visitor { @Override public void visitElectronic(Electronic electronic) { System.out.println("[价格] 电子产品《" + electronic.getName() + "》:单价" + electronic.getPrice() + "元/件"); }
@Override public void visitClothing(Clothing clothing) { System.out.println("[价格] 服装《" + clothing.getName() + "》:单价" + clothing.getPrice() + "元/件"); } }
|
3. 抽象元素(Element)
定义商品接受访问者的接口,声明accept方法(核心:让元素主动接受访问者):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| public abstract class Element { protected String name; protected double price; protected int quantity;
public Element(String name, double price, int quantity) { this.name = name; this.price = price; this.quantity = quantity; }
public abstract void accept(Visitor visitor);
public String getName() { return name; }
public double getPrice() { return price; }
public int getQuantity() { return quantity; } }
|
4. 具体元素(Concrete Element)
实现抽象元素接口,在accept方法中显式调用访问者对应的访问方法:
(1)电子产品(Electronic)
1 2 3 4 5 6 7 8 9 10 11 12
| public class Electronic extends Element { public Electronic(String name, double price, int quantity) { super(name, price, quantity); }
@Override public void accept(Visitor visitor) { visitor.visitElectronic(this); } }
|
(2)服装(Clothing)
1 2 3 4 5 6 7 8 9 10 11 12
| public class Clothing extends Element { public Clothing(String name, double price, int quantity) { super(name, price, quantity); }
@Override public void accept(Visitor visitor) { visitor.visitClothing(this); } }
|
5. 对象结构(Object Structure)
维护商品集合,提供遍历商品的接口,供访问者批量访问所有元素:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| import java.util.ArrayList; import java.util.List;
public class Inventory { private List<Element> elements = new ArrayList<>();
public void addElement(Element element) { elements.add(element); }
public void acceptVisitor(Visitor visitor) { for (Element element : elements) { element.accept(visitor); } } }
|
6. 客户端(Client)
创建库存、商品和访问者,触发访问操作,验证访问者模式的工作流程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public class Client { public static void main(String[] args) { Inventory inventory = new Inventory();
inventory.addElement(new Electronic("智能手机", 3999.0, 50)); inventory.addElement(new Electronic("笔记本电脑", 5999.0, 30)); inventory.addElement(new Clothing("纯棉衬衫", 199.0, 100)); inventory.addElement(new Clothing("休闲裤子", 249.0, 80));
System.out.println("===== 执行库存统计操作 ====="); StockCountVisitor countVisitor = new StockCountVisitor(); inventory.acceptVisitor(countVisitor); countVisitor.showTotal();
System.out.println("\n===== 执行价格打印操作 ====="); PricePrintVisitor printVisitor = new PricePrintVisitor(); inventory.acceptVisitor(printVisitor); } }
|
代码运行结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| ===== 执行库存统计操作 ===== [统计] 电子产品《智能手机》:50件,价值199950.0元 [统计] 电子产品《笔记本电脑》:30件,价值179970.0元 [统计] 服装《纯棉衬衫》:100件,价值19900.0元 [统计] 服装《休闲裤子》:80件,价值19920.0元 === 库存统计结果 === 总商品数量:260件 总商品价值:419740.0元
===== 执行价格打印操作 ===== [价格] 电子产品《智能手机》:单价3999.0元/件 [价格] 电子产品《笔记本电脑》:单价5999.0元/件 [价格] 服装《纯棉衬衫》:单价199.0元/件 [价格] 服装《休闲裤子》:单价249.0元/件
|
访问者模式核心逻辑解析
- 解耦操作与结构:商品元素(
Electronic/Clothing)仅维护自身属性,统计、打印等操作封装在访问者中,新增操作(如“折扣计算”)只需新增DiscountVisitor,无需修改商品类。
- 双重分派机制:通过“元素的
accept方法 + 访问者的visit方法”实现——先根据元素类型调用对应accept,再根据访问者类型调用对应visit,确保操作与元素精准匹配。
- 对象结构统一调度:
Inventory作为对象结构,批量管理商品并触发访问者,客户端只需与Inventory和访问者交互,无需关注单个商品的遍历逻辑。