设计模式——访问者模式

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

使用建议

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

注意事项

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

  • 对象结构要相对稳定
  • 不要让访问者过于复杂
  • 考虑使用双分派
相关推荐
资深web全栈开发1 小时前
设计模式之享元模式 (Flyweight Pattern)
设计模式·享元模式
驴儿响叮当20106 小时前
设计模式之原型模式
设计模式·原型模式
J_liaty9 小时前
23种设计模式一命令模式
设计模式·命令模式
J_liaty20 小时前
23种设计模式一迭代器模式
设计模式·迭代器模式
驴儿响叮当20101 天前
设计模式之策略模式
设计模式·策略模式
驴儿响叮当20101 天前
设计模式之中介模式
设计模式
驴儿响叮当20101 天前
设计模式之命令模式
设计模式·命令模式
驴儿响叮当20101 天前
设计模式之适配器模式
设计模式·适配器模式
HEU_firejef1 天前
设计模式——代理模式
设计模式·代理模式
Coder_Boy_1 天前
从单体并发工具类到分布式并发:思想演进与最佳实践(二)
java·spring boot·分布式·微服务·设计模式