设计模式——访问者模式

访问者模式 (Visitor Pattern)

什么是访问者模式?

访问者模式是一种行为型设计模式,它允许你在不修改对象结构的前提下定义作用于这些对象的新操作。

简单来说:访问者模式就是"访问者",可以在不修改对象结构的情况下添加新的操作。

生活中的例子

想象一下:

  • 博物馆:游客参观不同的展品
  • 体检:医生检查不同的身体部位
  • 税务:税务局检查不同的收入类型

为什么需要访问者模式?

传统方式的问题

java 复制代码
// 每次新增操作都需要修改类
class Element {
    public void operation1() {}
    public void operation2() {}
    // 新增操作需要修改类
    public void operation3() {}
}

问题:

  1. 违反开闭原则:修改现有类
  2. 代码臃肿:类中包含多个操作
  3. 难以扩展:新增操作需要修改多个类

访问者模式的优势

java 复制代码
// 使用访问者模式
element.accept(visitor);

优势:

  1. 符合开闭原则:不修改现有类
  2. 操作分离:操作与对象结构分离
  3. 易于扩展:新增操作很容易

访问者模式的结构

复制代码
┌─────────────────────┐
│     Visitor         │  访问者接口
├─────────────────────┤
│ + visit(ElementA):  │
│   void              │
│ + visit(ElementB):  │
│   void              │
└──────────┬──────────┘
           │ 实现
           │
┌──────────┴──────────┐
│ ConcreteVisitor     │  具体访问者
├─────────────────────┤
│ + visit(ElementA):  │
│   void              │
│ + visit(ElementB):  │
│   void              │
└─────────────────────┘

┌─────────────────────┐
│     Element         │  元素接口
├─────────────────────┤
│ + accept(Visitor):  │
│   void              │
└──────────┬──────────┘
           │ 实现
           ├──┬──────────────────┬──────────────┐
           │                    │              │
┌──────────┴──────┐  ┌───────────┴───────┐  ┌───┴────────┐
│  ElementA        │  │  ElementB         │  │ ...       │  具体元素
├─────────────────┤  ├───────────────────┤  ├────────────┤
│ + accept(): void│  │ + accept(): void  │  │            │
└─────────────────┘  └───────────────────┘  └────────────┘

代码示例

1. 定义访问者接口

java 复制代码
/**
 * 访问者接口
 */
public interface Visitor {
    /**
     * 访问图书
     */
    void visit(Book book);
    
    /**
     * 访问水果
     */
    void visit(Fruit fruit);
}

2. 定义元素接口

java 复制代码
/**
 * 元素接口
 */
public interface Element {
    /**
     * 接受访问者
     */
    void accept(Visitor visitor);
}

3. 定义具体元素

java 复制代码
/**
 * 具体元素:图书
 */
public class Book implements Element {
    private String name;
    private double price;
    
    public Book(String name, double price) {
        this.name = name;
        this.price = price;
    }
    
    public String getName() {
        return name;
    }
    
    public double getPrice() {
        return price;
    }
    
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

/**
 * 具体元素:水果
 */
public class Fruit implements Element {
    private String name;
    private double price;
    private double weight;
    
    public Fruit(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);
    }
}

4. 定义具体访问者

java 复制代码
/**
 * 具体访问者:购物车
 */
public class ShoppingCart implements Visitor {
    private List<Element> elements = new ArrayList<>();
    private double total = 0;
    
    public void addElement(Element element) {
        elements.add(element);
    }
    
    @Override
    public void visit(Book book) {
        double price = book.getPrice();
        total += price;
        System.out.println("图书: " + book.getName() + ",价格: ¥" + price);
    }
    
    @Override
    public void visit(Fruit fruit) {
        double price = fruit.getPrice() * fruit.getWeight();
        total += price;
        System.out.println("水果: " + fruit.getName() + ",价格: ¥" + price + " (¥" + fruit.getPrice() + "/kg × " + fruit.getWeight() + "kg)");
    }
    
    public double getTotal() {
        return total;
    }
}

5. 使用访问者

java 复制代码
/**
 * 访问者模式测试类
 * 演示如何使用访问者模式计算购物车总价
 */
public class VisitorTest {
    
    public static void main(String[] args) {
        System.out.println("=== 访问者模式测试 ===\n");
        
        // 创建购物车
        ShoppingCart cart = new ShoppingCart();
        
        // 添加商品
        Book book1 = new Book("Java编程思想", 89.0);
        Book book2 = new Book("设计模式", 69.0);
        Fruit apple = new Fruit("苹果", 8.0, 2.5);
        Fruit banana = new Fruit("香蕉", 5.0, 3.0);
        
        cart.addElement(book1);
        cart.addElement(book2);
        cart.addElement(apple);
        cart.addElement(banana);
        
        // 计算总价
        System.out.println("--- 计算购物车总价 ---");
        book1.accept(cart);
        book2.accept(cart);
        apple.accept(cart);
        banana.accept(cart);
        
        System.out.println("\n总价: ¥" + cart.getTotal());
        
        System.out.println("\n=== 访问者模式的优势 ===");
        System.out.println("1. 符合开闭原则:不修改现有类");
        System.out.println("2. 操作分离:操作与对象结构分离");
        System.out.println("3. 易于扩展:新增操作很容易");
        System.out.println("4. 集中管理:相关操作集中管理");
        
        System.out.println("\n=== 实际应用场景 ===");
        System.out.println("1. 编译器:语法树遍历");
        System.out.println("2. 文档处理:XML/JSON处理");
        System.out.println("3. 代码分析:代码静态分析");
        System.out.println("4. 对象结构:复杂对象结构的操作");
        
        System.out.println("\n=== 双分派 ===");
        System.out.println("访问者模式使用了双分派:");
        System.out.println("- 第一次分派:元素接受访问者");
        System.out.println("- 第二次分派:访问者访问元素");
        System.out.println("这样可以根据元素类型和访问者类型选择不同的操作");
    }
}

访问者模式的优点

  1. 符合开闭原则:不修改现有类
  2. 操作分离:操作与对象结构分离
  3. 易于扩展:新增操作很容易
  4. 集中管理:相关操作集中管理

访问者模式的缺点

  1. 增加复杂度:引入了额外的类
  2. 难以扩展:新增元素需要修改所有访问者
  3. 依赖具体:访问者依赖具体元素

适用场景

  1. 对象结构稳定:对象结构相对稳定
  2. 操作多变:操作经常变化
  3. 需要分离:需要将操作与对象结构分离

常见应用场景

  • 编译器:语法树遍历
  • 文档处理:XML/JSON处理
  • 代码分析:代码静态分析

使用建议

  • 对象结构稳定:使用访问者模式
  • 操作多变:使用访问者模式
  • 对象结构多变:使用其他模式

注意事项

⚠️ 访问者模式虽然强大,但要注意:

  • 对象结构要相对稳定
  • 不要让访问者过于复杂
  • 考虑使用双分派
相关推荐
Zane19943 天前
一个 new 就能创建对象,为什么还要拆出单例、工厂、建造者、原型四种模式?
设计模式
怕浪猫4 天前
一行命令复刻爆款视频,我把 Hypit 从安装跑到了出片
人工智能·设计模式·程序员
Zane19944 天前
函数式编程里的函数,其实不是你天天写的那个函数——三大编程范式的边界在哪
设计模式
Zane19945 天前
策略模式现在该不该上?一次讲清楚过度设计和设计不足怎么找平衡
设计模式
她说..5 天前
常见设计模式-模板方法模式
java·spring·设计模式·springboot
xiaofeiyang1506 天前
第六章 · 桥接 — 三支毛笔,画出九种颜色
设计模式
Shadow(⊙o⊙)6 天前
OTOL设计模式 One Thread One Loop
服务器·网络·设计模式
sarasuki7 天前
如何让LLM 能在半夜偷偷打开网易云呢?
人工智能·设计模式·agent
小王师傅667 天前
【设计模式】装饰模式(四):框架源码实战——从 Java I/O 到 Spring 到 MyBatis
java·设计模式
执明wa7 天前
Android RecyclerView 多类型, 多种 Item
android·xml·开发语言·设计模式·android studio