【设计模式】访问者模式

发布于:2025-04-13 ⋅ 阅读:(86) ⋅ 点赞:(0)

**简介
假设你有一个购物车(对象结构),里面有多种商品(元素),如苹果、牛奶、书籍。每个商品的计价规则不同:

  • 水果按重量计价
  • 牛奶按数量计价
  • 书籍按固定价格计价
    现在需要实现两种功能:
  1. 计算总价
  2. 打印购物小票

访问者模式的作用

  • 将商品和操作(如计算价格、打印小票)分离。
  • 新增功能(如打折计算)时,只需新增一个“访问者”,无需修改商品类。

适用场景

  • 对象结构稳定,但需要频繁新增操作。
  • 需要对同一对象结构进行多种独立操作(如计算总价、导出数据)。

优点

  • 符合开闭原则:新增操作无需修改对象结构。
  • 操作逻辑集中:每个访问者类负责一个独立功能。

缺点

  • 对象结构变化时,所有访问者类都需要修改。
  • 过度使用会导致代码复杂度增加。

代码

// 元素接口:商品
interface ItemElement {
    int accept(ShoppingCartVisitor visitor);
}

// 具体元素:书籍
class Book implements ItemElement {
    private int price;
    private String isbn;
    public Book(int price, String isbn) {
        this.price = price;
        this.isbn = isbn;
    }
    public int accept(ShoppingCartVisitor visitor) {
        return visitor.visit(this);
    }
    public int getPrice() { return price; }
}

// 具体元素:水果
class Fruit implements ItemElement {
    private int pricePerKg;
    private int weight;
    public Fruit(int pricePerKg, int weight) {
        this.pricePerKg = pricePerKg;
        this.weight = weight;
    }
    public int accept(ShoppingCartVisitor visitor) {
        return visitor.visit(this);
    }
    public int getPricePerKg() { return pricePerKg; }
    public int getWeight() { return weight; }
}

// 访问者接口
interface ShoppingCartVisitor {
    int visit(Book book);
    int visit(Fruit fruit);
}

// 具体访问者:计算总价
class ShoppingCartVisitorImpl implements ShoppingCartVisitor {
    public int visit(Book book) {
        return book.getPrice();
    }
    public int visit(Fruit fruit) {
        return fruit.getPricePerKg() * fruit.getWeight();
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        ItemElement[] items = new ItemElement[]{
            new Book(100, "ISBN-123"),
            new Fruit(10, 2)
        };

        ShoppingCartVisitor visitor = new ShoppingCartVisitorImpl();
        int total = 0;
        for (ItemElement item : items) {
            total += item.accept(visitor);
        }
        System.out.println("Total cost: " + total); // 输出 120
    }
}

类图
在这里插入图片描述

@startuml
interface ItemElement {
    + accept(ShoppingCartVisitor visitor): int
}

class Book {
    - price: int
    - isbn: String
    + accept(ShoppingCartVisitor visitor): int
}

class Fruit {
    - pricePerKg: int
    - weight: int
    + accept(ShoppingCartVisitor visitor): int
}

interface ShoppingCartVisitor {
    + visit(Book book): int
    + visit(Fruit fruit): int
}

class ShoppingCartVisitorImpl {
    + visit(Book book): int
    + visit(Fruit fruit): int
}

ItemElement <|-- Book
ItemElement <|-- Fruit
ShoppingCartVisitor <|-- ShoppingCartVisitorImpl
Book --> ShoppingCartVisitor
Fruit --> ShoppingCartVisitor
@enduml

4. 实际应用场景案例:编译器语法树分析

场景:编译器需要遍历抽象语法树(AST),进行类型检查、代码生成等操作。

// 表达式元素接口
interface Expr {
    <T> T accept(Visitor<T> visitor);
}

// 具体元素:数字表达式
class Number implements Expr {
    private int value;
    public Number(int value) { this.value = value; }
    public <T> T accept(Visitor<T> visitor) {
        return visitor.visit(this);
    }
    public int getValue() { return value; }
}

// 具体元素:加法表达式
class Addition implements Expr {
    private Expr left;
    private Expr right;
    public Addition(Expr left, Expr right) {
        this.left = left;
        this.right = right;
    }
    public <T> T accept(Visitor<T> visitor) {
        return visitor.visit(this);
    }
    public Expr getLeft() { return left; }
    public Expr getRight() { return right; }
}

// 访问者接口
interface Visitor<T> {
    T visit(Number number);
    T visit(Addition addition);
}

// 具体访问者:表达式求值
class EvaluateVisitor implements Visitor<Integer> {
    public Integer visit(Number number) {
        return number.getValue();
    }
    public Integer visit(Addition addition) {
        return addition.getLeft().accept(this) + addition.getRight().accept(this);
    }
}

// 客户端代码
public class CompilerClient {
    public static void main(String[] args) {
        Expr expr = new Addition(
            new Number(10),
            new Addition(new Number(20), new Number(30))
        );
        EvaluateVisitor evaluator = new EvaluateVisitor();
        int result = expr.accept(evaluator);
        System.out.println("Result: " + result); // 输出 60
    }
}

网站公告

今日签到

点亮在社区的每一天
去签到