当前位置: 首页 > news >正文

【设计模式】访问者模式

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

  • 水果按重量计价
  • 牛奶按数量计价
  • 书籍按固定价格计价
    现在需要实现两种功能:
  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
    }
}

相关文章:

  • 微软庆祝它成立整整50周年
  • 将图片按照指定大小批量进行裁剪(可设置步长_python)
  • 怎样使用Python编写的Telegram聊天机器人
  • Android compose源码浅析——Modifier
  • Python第八章03:Pyecharts快速入门
  • excel中的VBA指令示例(一)
  • 考研单词笔记 2025.04.09
  • docker 运行自定义化的服务-前端
  • Memcached缓存系统:从部署到实战应用指南
  • 银河麒麟系统虚拟机网络ping不通的解决方法
  • 题目练习之位运算
  • OpenAI Operator:开启 AI 智能体的无限可能
  • 力扣DAY40-45 | 热100 | 二叉树:直径、层次遍历、有序数组->二叉搜索树、验证二叉搜索树、二叉搜索树中第K小的元素、右视图
  • 8个方向使用DeepSeek打磨完美课题申报书!
  • 我谈冈萨雷斯关于离散傅里叶变换(DFT)的两点误解
  • esp32cam -> 服务器 | 手机 -> 服务器 直接服务器传输图片
  • 从 0 到上线:Java 项目打包 Docker 镜像全流程实战
  • 宁德时代25年校招演绎数字推理SHL测评题库题型及真题分析
  • 机器学习01-支持向量机(SVM)(未完)
  • 【从零开始学习JVM | 第一篇】快速认识JVM
  • 做企业网站织梦和wordpress哪个好/站长之家怎么用
  • 新建网站如何做关键词/营销型网站建设怎么做
  • 用织梦系统做的2个网站要把它都上传到服务器上吗/seo自动点击排名
  • 做短租类型的网站/百度一下百度主页官网
  • 四川 网站建设/百度关键词搜索技巧
  • 做网站baidunongmin/留号码的广告网站不需要验证码