公司动态
Java 23 种设计模式:从踩坑到精通 | 番外:组合 vs 解释器 —— 结构树 vs 计算树
Java 23 种设计模式从踩坑到精通 | 番外组合 vs 解释器 —— 结构树 vs 计算树摘要组合模式和解释器模式都用到了树形结构但组合模式的树是“结构树”——叶子与容器行为统一客户端一致处理解释器模式的树是“计算树”——每种节点有独立的求值逻辑通过递归计算得出结果。本文从文件系统管理器和四则运算计算器两个场景出发结合代码对比、UML 分析和 JDKPattern、Spring SpEL 源码帮你彻底分清这对“树形结构型”模式。️本文阅读地图3 分钟速览为什么文件系统是组合模式表达式求值是解释器模式UML 对比统一操作 vs 递归求值手写文件系统管理器 vs 四则运算计算器看代码感受差异组合模式中的operation()和解释器中的interpret()本质区别面试必问“组合和解释器都用了树到底有什么区别”《Java 23 种设计模式从踩坑到精通》开篇系列介绍与目录 当前番外 · 组合 vs 解释器 返回系列总目录文章目录Java 23 种设计模式从踩坑到精通 | 番外组合 vs 解释器 —— 结构树 vs 计算树1. 从“文件系统”和“四则运算”两个场景说起1.1 场景一文件系统管理器组合模式1.2 场景二四则运算计算器解释器模式1.3 关键差异已浮现2. UML 对比3. 三大核心区别4. JDK 源码深度对比4.1 AWT 组件树组合模式4.2 Java 正则表达式解释器模式5. 组合模式可以作为解释器模式的“基础结构”6. 面试必问 追问连环炮 《Java 23 种设计模式从踩坑到精通》快速导航1. 从“文件系统”和“四则运算”两个场景说起1.1 场景一文件系统管理器组合模式假设你在开发一个文件管理系统需要展示文件夹和文件的层级结构。文件夹里可以有文件也可以有子文件夹。你需要递归展示整个目录树但不管是文件还是文件夹对客户端来说最好能统一操作。// 组合模式统一处理叶子文件和容器文件夹FileSystemNoderootnewFolderNode(root);FileSystemNodedocsnewFolderNode(docs);FileSystemNodefile1newFileNode(readme.txt);FileSystemNodefile2newFileNode(image.png);root.add(docs);docs.add(file1);docs.add(file2);// 客户端不用区分文件和文件夹统一调用 show()root.show(0);// 输出// 文件夹root// 文件夹docs// 文件readme.txt// 文件image.png白话客户端只调用show()不管是文件还是文件夹都照常执行。组合模式的核心是统一叶子与容器的接口让客户端一致处理。1.2 场景二四则运算计算器解释器模式现在你要实现一个表达式计算器能够解析并计算a b - c这样的表达式。变量a、b、c是叶子节点、-是运算符节点。你需要根据语法规则递归求值。// 解释器模式构建语法树递归求值ContextctxnewContext();ctx.setVariable(a,10);ctx.setVariable(b,5);ctx.setVariable(c,2);// 构建语法树a b - cExpressionexpressionnewSubtract(newAdd(newVariable(a),newVariable(b)),newVariable(c));intresultexpression.interpret(ctx);System.out.println(a b - c result);// 13白话Add节点和Subtract节点各自有自己的求值逻辑Variable节点直接取值。每种节点的interpret()行为完全不同。解释器模式的核心是每种节点有独立的计算逻辑通过递归得出最终结果。1.3 关键差异已浮现组合模式文件和文件夹的show()行为相似——都是展示自己文件夹多了遍历子节点解释器模式Add.interpret()做加法Subtract.interpret()做减法Variable.interpret()取值——行为完全不同。2. UML 对比3. 三大核心区别对比维度组合模式解释器模式树的性质结构树——表示部分-整体层次关系计算树——表示语法规则和求值逻辑节点行为operation()统一——叶子与容器行为相似interpret()各不相同——每种节点求值逻辑不同是否管理子节点✅ 容器有add()/remove()❌ 节点不暴露管理方法客户端操作调用顶层节点的方法递归遍历整棵树调用顶层节点的interpret()递归计算出结果典型应用文件系统、组织架构、AWT 组件树四则运算、正则表达式、Spring SpEL4. JDK 源码深度对比4.1 AWT 组件树组合模式// AWT 中Panel 是容器Button 是叶子都继承自 Component// add() 方法在容器中管理子组件paint() 递归渲染整棵组件树PanelpanelnewPanel();ButtonbtnnewButton(点击);panel.add(btn);// 容器管理子组件panel.paint(graphics);// 递归渲染所有子组件为什么是组合模式Button和Panel都继承自Component接口统一Panel通过add()管理子组件Button没有子组件调用paint()时Panel递归调用所有子组件的paint()——行为一致。4.2 Java 正则表达式解释器模式// Pattern 内部将正则表达式编译为一棵语法树PatternpatternPattern.compile(a(b|c)*d);Matchermatcherpattern.matcher(abcd);booleanmatchesmatcher.matches();为什么是解释器模式正则表达式a(b|c)*d被解析为一棵语法树a、b、c、d是终结符字符字面量|和*是非终结符运算符匹配时递归遍历语法树每种节点字符、选择、重复执行不同的匹配逻辑——行为各不相同。5. 组合模式可以作为解释器模式的“基础结构”解释器模式通常建立在组合模式之上——语法树本身就是一种树形结构非终结符节点包含子表达式这与组合模式的容器-叶子结构高度相似。对比维度组合模式解释器模式关注点结构的组织方式结构的计算逻辑组合关系独立使用通常建立在组合模式之上面试常见问法“如何统一处理树形结构”“如何设计一个表达式解析器”一句话总结组合模式负责“搭架子”树形结构解释器模式负责“填逻辑”每种节点的计算规则。Spring SpEL 就是先构建组合树再递归求值的典型代表。6. 面试必问 追问连环炮基础必问组合模式和解释器模式都用到了树有什么区别 组合关注“结构统一”——叶子与容器行为一致客户端统一操作解释器关注“递归求值”——每种节点有不同的计算逻辑。什么时候用组合什么时候用解释器 需要统一处理树形结构文件系统、菜单→ 组合需要定义语法规则并求值表达式、规则引擎→ 解释器。面试官追问“解释器模式和组合模式能配合使用吗” 可以。解释器模式的语法树通常建立在组合模式之上——用组合模式管理树结构用解释器模式实现每个节点的求值逻辑。Spring SpEL 就是这样做的。“为什么复杂语法不推荐解释器模式” 因为每个语法规则都要对应一个类类数量会爆炸类膨胀问题递归求值性能远低于直接编译。此时应用 ANTLR、JavaCC 等专业解析器生成工具。“组合模式中如果只想遍历叶子节点怎么办” 在 Component 接口中定义isLeaf()方法或使用访问者模式对不同节点类型分别处理。恭喜如果你能立刻画出文件系统的树形结构和四则运算的语法树并说出“组合关注结构统一解释器关注递归求值”你已经掌握了这两棵树最核心的区别。延伸阅读本文是组合模式与解释器模式的深度对比。如果你想先了解每种模式的完整原理、UML、代码实现和面试题可以先阅读正篇——组合模式树形结构处理部分与整体一视同仁 和 解释器模式自己动手写一个小语言解释器再回来看这篇对比效果更佳。 《Java 23 种设计模式从踩坑到精通》快速导航开篇系列介绍与目录当前番外 · 组合 vs 解释器 —— 结构树 vs 计算树你在这里 延伸阅读组合模式正篇 解释器模式正篇创建型模式汇总结构型模式汇总行为型模式汇总 关注《Java 23 种设计模式从踩坑到精通》用 25 篇文章彻底吃透设计模式。福利预告全系列代码及 UML 源码将在完结时统一打包开放点击「关注」「收藏」第一时间获取。 除了设计模式我也在深挖智能物流实战WMS、托盘调度、机器学习落地。欢迎点击头像看看专栏 《出版社物流WMS智能调度实战》。技术相通思路可鉴。