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

设计模式-visit模式-在语法树的实践

文章目录

      • 背景
      • 示例代码
      • 分析
        • 灵活性
        • 双重分派
      • 总结

背景

很多项目代码有accept()用法,在calcite 里也看到了这种,深入了解一下
语法树遍历:编译器通常会将源代码解析成抽象语法树(AST)。为了实现不同的编译阶段,如语法分析、类型检查、代码生成等,访问者模式非常有用。每个阶段可以有自己的访问者类,而无需修改语法树的结构。
例子:一个编译器可以有 TypeCheckVisitor 用于类型检查,CodeGenVisitor 用于生成目标代码。

示例代码

RexNode 接口的源码有accept方法

public abstract class RexNode {/*** Accepts a visitor, dispatching to the right overloaded* {@link RexVisitor#visitInputRef visitXxx} method.** <p>Also see {@link RexUtil#apply(RexVisitor, java.util.List, RexNode)},* which applies a visitor to several expressions simultaneously.*/public abstract <R> R accept(RexVisitor<R> visitor);
}

定义一个visitor类

package com.demo;import org.apache.calcite.rex.*;public class CustomRexVisitor extends RexVisitorImpl<Void> {public CustomRexVisitor() {super(true);  // true 表示遍历整个树}@Overridepublic Void visitInputRef(RexInputRef inputRef) {System.out.println("Visiting input reference: " + inputRef.getIndex());return null;}@Overridepublic Void visitLiteral(RexLiteral literal) {System.out.println("Visiting literal: " + literal.getValue3());return null;}@Overridepublic Void visitCall(RexCall call) {System.out.println("Visiting call: " + call.getOperator().getName());// 继续遍历子表达式for (RexNode operand : call.getOperands()) {operand.accept(this);}return null;}// 可以重写更多的方法来处理其他类型的节点
}

定义


import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeFactory;
import org.apache.calcite.rel.type.RelDataTypeSystemImpl;
import org.apache.calcite.rex.RexBuilder;
import org.apache.calcite.rex.RexInputRef;
import org.apache.calcite.rex.RexLiteral;
import org.apache.calcite.rex.RexNode;
import org.apache.calcite.sql.fun.SqlStdOperatorTable;
import org.apache.calcite.sql.type.SqlTypeFactoryImpl;
import org.apache.calcite.sql.type.SqlTypeName;public class RexVisitorExample {public static void main(String[] args) {// 创建类型工厂RelDataTypeFactory typeFactory = new SqlTypeFactoryImpl(new RelDataTypeSystemImpl() { });// 创建字段类型和字面量类型RelDataType intType = typeFactory.createSqlType(SqlTypeName.INTEGER);// 创建字段引用和字面量RexBuilder rexBuilder = new RexBuilder(typeFactory);RexInputRef fieldRef = rexBuilder.makeInputRef(intType, 0);  // 对应字段aRexLiteral literal = rexBuilder.makeLiteral(10, intType);// 创建加法运算表达式RexNode expression = rexBuilder.makeCall(SqlStdOperatorTable.PLUS, fieldRef, literal);// 使用自定义访问者遍历表达式树CustomRexVisitor visitor = new CustomRexVisitor();expression.accept(visitor);}
}

分析

RexNode 有很多种实现,实际上就是打印出来就是一颗语法树,然把在visit 接口类增加对每种数据结构的访问方法,把要实现的具体操作放到visitor 实现类去,达到数据结构和操作之间的解偶。

灵活性

访问者模式的核心思想是通过将操作封装在访问者中,使得可以在不修改数据结构的情况下添加新的操作。这一点通过 accept 方法得以实现。

expression.accept(visitor) 这一调用让表达式树的节点来“接受”一个访问者对象,实际上是节点把自己传递给访问者,由访问者来决定如何处理这个节点。
这意味着访问策略是动态决定的,具体的操作逻辑不再由节点自己决定,而是由传入的访问者对象决定。

双重分派

这个设计背后的一个重要概念是双重分派(Double Dispatch)。
单分派:在普通方法调用中,调用方法的对象类型决定了调用哪个方法,这是一次分派。
双重分派:在访问者模式中,accept 方法的调用对象(即节点)和传入的访问者对象的类型共同决定了最终调用的具体方法。这就是两次分派,或者说双重分派。
在 expression.accept(visitor) 这行代码中:
第一次分派:调用节点的 accept 方法时,由表达式树中的具体节点类型(如 RexLiteral 或 RexCall)决定。
第二次分派:节点将自己传递给访问者,访问者根据节点的具体类型(如 RexLiteral、RexCall 等)选择对应的处理方法(如 visitLiteral、visitCall)。
这意味着操作逻辑不仅依赖于节点的类型,还依赖于传入访问者的类型和访问者的逻辑,从而实现灵活且可扩展的处理方式。

总结

访问者模式和类似的策略模式在面对需要对对象结构进行多种操作时非常有用。它们帮助你在不修改对象结构的情况下增加新的操作逻辑,使代码更容易维护和扩展。这些模式特别适用于那些操作复杂、多变、且具有层次结构的数据结构的场景。

http://www.lryc.cn/news/429556.html

相关文章:

  • ZK-Rollups测评
  • redis生产使用场景(一):并行流+二级缓存
  • EXCEL跨文件查询,指定条件列,返回满足条件的指定列
  • [数据集][目标检测]流水线物件检测数据集VOC+YOLO格式9255张26类别
  • StarRocks 存算分离 Compaction 原理
  • 搭建ELK日志采集与分析系统
  • java集合中自动排序的treeset和treemap
  • Android 修改SystemUI 音量条的声音进度条样式
  • 电商场景的视频生成的prompt测评集合
  • day34
  • 无缝融入,即刻智能[三]:Dify-LLM平台知识库构建(多路召回、精排重排),43K+星标见证专属智能方案
  • AWS服务WAF
  • 二叉树中的奇偶树问题
  • GD - EmbeddedBuilder - 用DMA进行串口发送接收,支持接收不定长包
  • 英语中apartment(公寓)(美式)、house(房子)、flat(公寓)(英式)、villa(别墅)、room(房间)区别
  • 黑马头条vue2.0项目实战(十一)——功能优化(组件缓存、响应拦截器、路由跳转与权限管理)
  • 《AI视频类工具之一——​ 即创》
  • CSS的:host伪类:精确定位于Web组件的指南
  • 安卓sdk manager下载安装
  • CV学习笔记3-图像特征提取
  • Git使用方法(三)---简洁版上传git代码
  • 8.21-部署eleme项目
  • 多目标跟踪之ByteTrack论文(翻译+精读)
  • 【实践】Java开发常用工具类或中间件
  • Vue2移动端(H5项目)项目封装车牌选择组件
  • 四川财谷通信息技术有限公司抖音小店的优势
  • 2025届八股文:计算机网络高频重点面试题
  • 嵌入式和单片机有什么区别?
  • JSON.stringify 和 JSON.parse
  • APP架构设计_2.用MVVM架构实现一个具体业务