设计模式——访问者模式

访问者模式 (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处理
  • 代码分析:代码静态分析

使用建议

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

注意事项

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

  • 对象结构要相对稳定
  • 不要让访问者过于复杂
  • 考虑使用双分派
相关推荐
程序员Terry1 天前
同事被深拷贝坑了3小时,我教他原型模式的正确打开方式
java·设计模式
刀法如飞2 天前
AI时代,程序员都应该是算法思想工程师
人工智能·设计模式·程序员
在西安放羊的牛油果2 天前
我把 2000 行下单代码,重构成了一套交易前端架构
前端·设计模式·架构
寅时码3 天前
React 正在演变为一场不可逆的赛博瘟疫:AI 投毒、编译器迷信与装死的官方
前端·react.js·设计模式
willow6 天前
Axios由浅入深
设计模式·axios
七月丶8 天前
别再手动凑 PR 了:这个 AI Skill 会按仓库习惯自动建分支、拆提交、提 PR
人工智能·设计模式·程序员
刀法如飞8 天前
从程序员到架构师:6大编程范式全解析与实践对比
设计模式·系统架构·编程范式
九狼8 天前
Flutter + Riverpod +MVI 架构下的现代状态管理
设计模式
静水流深_沧海一粟9 天前
04 | 别再写几十个参数的构造函数了——建造者模式
设计模式