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

【设计模式】【行为型模式】解释器模式(Interpreter)

👋hi,我不是一名外包公司的员工,也不会偷吃茶水间的零食,我的梦想是能写高端CRUD
🔥 2025本人正在沉淀中… 博客更新速度++
👍 欢迎点赞、收藏、关注,跟上我的更新节奏
🎵 当你的天空突然下了大雨,那是我在为你炸乌云

文章目录

  • 一、入门
    • 什么是解释器模式?
    • 为什么要解释器模式?
    • 如何实现解释器模式?
  • 二、解释器模式在框架源中的运用
    • Spring 表达式语言(SpEL)
  • 三、总结
    • 解释器模式的优点
    • 解释器模式的缺点
    • 解释器模式的适用场景

一、入门

什么是解释器模式?

解释器模式(Interpreter Pattern)是一种行为设计模式,用于定义语言的语法表示,并提供一个解释器来处理该语法。它通常用于需要解释和执行特定语言或表达式的场景。

为什么要解释器模式?

假设一个电商平台需要实现动态的促销规则,例如:

  1. 规则1:用户是 VIP 且 订单金额 ≥ 100 元 → 享受 20 元优惠。
  2. 规则2:商品类别是 “电子产品” 或 库存量 > 50 → 允许参加秒杀活动。

这些规则需要灵活配置,并且随着业务发展可能会新增条件(例如添加“用户年龄 ≤ 30 岁”等)。

下面是没有用解释器模式的实现代码。

public class PromotionRuleWithoutInterpreter {public static boolean checkRule1(Map<String, Object> context) {boolean isVip = (boolean) context.get("isVip");double orderAmount = (double) context.get("orderAmount");return isVip && orderAmount >= 100;}public static boolean checkRule2(Map<String, Object> context) {String category = (String) context.get("productCategory");int stock = (int) context.get("stock");return category.equals("electronics") || stock > 50;}// 每新增一个规则,都需要添加一个新方法,且逻辑无法复用!
}

存在问题

  1. 重复代码:每个规则都需要手动解析字段和逻辑。
  2. 难以扩展:新增规则需要修改代码,违反开闭原则。
  3. 维护困难:如果字段名或条件逻辑变化,需要修改所有相关方法。

如何实现解释器模式?

解释器模式的构成

  1. 抽象表达式(Abstract Expression):定义解释操作的接口,通常包含一个interpret()方法。
  2. 终结符表达式(Terminal Expression):实现与语法中的终结符相关的解释操作。
  3. 非终结符表达式(Non-terminal Expression):实现语法中的规则,通常包含对其他表达式的引用。
  4. 上下文(Context):包含解释器需要的全局信息。
  5. 客户端(Client):构建语法树并调用解释操作。主要讲需要分析的句子或表达式转换成解释器对象描述的抽象语法树,然后调用解释器的解释方法,当然也可以通过环节角色间访问解释器的解释方法。

【案例】大促规则 - 改
在这里插入图片描述
抽象表达式(Abstract Expression)RuleExpression接口。

interface RuleExpression {boolean interpret(Map<String, Object> context);
}

终结符表达式(Terminal Expression): IsVipExpressio类,判断用户是否为vip;OrderAmountExpression判断订单金额是否≥指定值;ProductCategoryExpression类,判断商品类别是否匹配。

// 终结符表达式:用户是否是VIP
class IsVipExpression implements RuleExpression {@Overridepublic boolean interpret(Map<String, Object> context) {return (boolean) context.get("isVip");}
}// 终结符表达式:订单金额是否≥指定值
class OrderAmountExpression implements RuleExpression {private double minAmount;public OrderAmountExpression(double minAmount) {this.minAmount = minAmount;}@Overridepublic boolean interpret(Map<String, Object> context) {double amount = (double) context.get("orderAmount");return amount >= minAmount;}
}// 终结符表达式:商品类别是否匹配
class ProductCategoryExpression implements RuleExpression {private String category;public ProductCategoryExpression(String category) {this.category = category;}@Overridepublic boolean interpret(Map<String, Object> context) {String actualCategory = (String) context.get("productCategory");return actualCategory.equals(category);}
}

非终结符表达式(组合条件)AndExpression类,逻辑"与"操作。OrExpression类,逻辑"或"操作。

// 非终结符表达式:逻辑"与"操作
class AndExpression implements RuleExpression {private RuleExpression expr1;private RuleExpression expr2;public AndExpression(RuleExpression expr1, RuleExpression expr2) {this.expr1 = expr1;this.expr2 = expr2;}@Overridepublic boolean interpret(Map<String, Object> context) {return expr1.interpret(context) && expr2.interpret(context);}
}// 非终结符表达式:逻辑"或"操作
class OrExpression implements RuleExpression {private RuleExpression expr1;private RuleExpression expr2;public OrExpression(RuleExpression expr1, RuleExpression expr2) {this.expr1 = expr1;this.expr2 = expr2;}@Overridepublic boolean interpret(Map<String, Object> context) {return expr1.interpret(context) || expr2.interpret(context);}
}

客户端,假设需要判断用户是否满足,规则1(VIP 且订单金额≥100元),客户端是负责 构建规则表达式 并 调用解释器执行规则 的部分。

public class PromotionRuleDemo {public static void main(String[] args) {// 上下文数据(模拟用户订单信息)Map<String, Object> context = new HashMap<>();context.put("isVip", true);context.put("orderAmount", 150.0);// 构建规则表达式:isVip && orderAmount >= 100RuleExpression rule1 = new AndExpression(new IsVipExpression(),new OrderAmountExpression(100.0));// 解释并执行规则boolean canApplyDiscount = rule1.interpret(context);System.out.println("是否满足促销规则1: " + canApplyDiscount); // 输出: true}
}

改造后的好处

  1. 规则可配置化
    可以将促销规则抽象为表达式对象,甚至通过配置文件或数据库动态生成规则树,无需修改代码。
    例如:将规则 (isVip && orderAmount >= 100) || (productCategory == "electronics") 存储为 JSON,动态解析为表达式对象。
  2. 灵活组合条件
    通过组合AndExpressionOrExpression,可以轻松实现复杂的逻辑。
  3. 易于扩展
    新增条件(例如“用户年龄 ≤ 30”)只需添加新的终结符表达式,无需改动现有代码。

二、解释器模式在框架源中的运用

Spring 表达式语言(SpEL)

Spring 的 SpEL(Spring Expression Language)允许在运行时解析字符串表达式(如 "user.name""price * quantity"),并绑定到对象属性或执行逻辑。其底层实现使用了解释器模式的思想。
下面的代码时SpEL在 @Value 注解中注入动态值

@Component
public class AppConfig {// 注入配置文件中的值@Value("${app.name}")private String appName;// 使用 SpEL 计算值@Value("#{ T(java.lang.Math).random() * 100.0 }")private double randomNumber;// 引用其他 Bean 的属性@Value("#{userService.defaultUser.name}")private String defaultUserName;
}

SpEL 的核心流程分为两个阶段

  1. 解析阶段:将字符串表达式(如 “2 + 3 * 4”)解析为 抽象语法树(AST),树中的每个节点对应一个表达式对象。
  2. 执行阶段:递归遍历 AST,解释每个节点并计算结果。这一过程完美契合解释器模式的 语法树解释执行 思想。
     OpPlus/    \"2"   OpMultiply/    \"3"    "4"

下面是对源码的分析:

抽象表达式Expression 接口,所有具体表达式(如字面量、运算符、方法调用)都实现此接口。

public interface Expression {// 核心方法:解释表达式并返回结果Object getValue() throws EvaluationException;// 其他重载方法(支持上下文、目标类型等)
}

终结符表达式示例LiteralExpressionLiteralExpression 直接解析字面量(如 “100”),无需依赖其他表达式。

public class LiteralExpression implements Expression {private final String literalValue;public LiteralExpression(String literalValue) {this.literalValue = literalValue;}@Overridepublic Object getValue() {// 直接返回字面量值(如 "42" 转换为整数)return this.literalValue;}
}

非终结符表达式示例OpPlus(加法操作)。OpPlus 组合了左、右两个操作数(可能是其他表达式对象),递归解释执行。

public class OpPlus extends Operator {@Overridepublic TypedValue getValueInternal(ExpressionState state) throws EvaluationException {// 递归获取左、右操作数的值Object leftOperand = getLeftOperand().getValueInternal(state).getValue();Object rightOperand = getRightOperand().getValueInternal(state).getValue();// 执行加法操作return new TypedValue(leftOperand + rightOperand);}
}

上下文EvaluationContext接口,StandardEvaluationContext 是默认实现,提供变量绑定和类型支持。

public interface EvaluationContext {// 获取变量值(如 "#user")Object lookupVariable(String name);// 获取类型转换器、函数等TypeConverter getTypeConverter();
}

客户端SpelExpressionParser:SpelExpressionParser 负责将字符串转换为 Expression 对象(语法树的根节点)。

public class SpelExpressionParser {// 解析字符串为 Expression 对象(语法树)public Expression parseExpression(String expressionString) {// 使用 Tokenizer 分词,Parser 构建 ASTreturn this.doParseExpression(expressionString);}
}

测试类

public class SpELAdditionExample {public static void main(String[] args) {// 1. 创建 SpEL 解析器ExpressionParser parser = new SpelExpressionParser();// 2. 解析加法表达式Expression expr = parser.parseExpression("2 + 3 * 4");// 3. 执行表达式并获取结果Integer result = expr.getValue(Integer.class);// 4. 输出结果System.out.println("计算结果: " + result); // 输出: 计算结果: 14}
}

三、总结

解释器模式的优点

  1. 易于扩展语法规则
    通过添加新的表达式类,可以轻松扩展语法规则,符合 开闭原则(对扩展开放,对修改封闭)。
  2. 实现简单语法解析
    对于简单的语法规则,解释器模式提供了一种直观的实现方式,将语法规则分解为多个表达式类。
  3. 解耦语法解析与执行
    将语法解析逻辑封装在表达式类中,与业务逻辑解耦,使代码更清晰、更易维护。
  4. 适合领域特定语言(DSL)
    解释器模式非常适合实现 领域特定语言(如规则引擎、查询语言等),能够将复杂的业务规则抽象为表达式树。
  5. 灵活性
    可以通过组合不同的表达式类,动态构建复杂的语法树,支持运行时修改规则。

解释器模式的缺点

  1. 复杂性高
    对于复杂的语法规则,解释器模式会导致类的数量急剧增加(每个规则都需要一个表达式类),增加系统复杂性。
  2. 性能问题
    解释器模式通常采用递归解释执行,性能较低,不适合对性能要求较高的场景。
  3. 难以维护
    随着语法规则的增加,表达式类的数量会变得庞大,导致代码难以维护。
  4. 不适合复杂语法
    解释器模式更适合处理简单的语法规则,对于复杂的语法(如编程语言),使用解释器模式会变得非常笨拙。
  5. 学习成本高
    需要开发者熟悉语法树的设计和递归解释执行的原理,增加了学习和实现的难度。

解释器模式的适用场景

  1. 领域特定语言(DSL)
    当需要实现一种简单的领域特定语言时,解释器模式是一种自然的选择。例如:
    • 规则引擎(如促销规则、风控规则)。
    • 查询语言(如 SQL 条件解析)。
    • 模板引擎(如动态生成邮件内容)。
  2. 需要动态解析和执行规则的场景
    当规则需要动态配置(如从数据库或配置文件中加载)并在运行时解析执行时,解释器模式非常适用。
  3. 语法规则相对固定且简单
    如果语法规则不会频繁变化,且规则数量较少,解释器模式可以很好地满足需求。
  4. 不适合使用编译器或解析器生成工具的场景
    对于简单的语法规则,使用编译器或解析器生成工具(如 ANTLR)可能过于复杂,解释器模式提供了一种轻量级的解决方案。

相关文章:

【设计模式】【行为型模式】解释器模式(Interpreter)

&#x1f44b;hi&#xff0c;我不是一名外包公司的员工&#xff0c;也不会偷吃茶水间的零食&#xff0c;我的梦想是能写高端CRUD &#x1f525; 2025本人正在沉淀中… 博客更新速度 &#x1f44d; 欢迎点赞、收藏、关注&#xff0c;跟上我的更新节奏 &#x1f3b5; 当你的天空突…...

修改OnlyOffice编辑器默认字体

通过Docker修改OnlyOffice编辑器默认字体 问题描述详细方案1. 删除原生字体文件2. 创建字体目录3. 复制字体文件到容器中4. 执行字体更新脚本5. 重新启动容器 注意事项 问题描述 在OnlyOffice中&#xff0c;编辑器的默认字体可能不符合公司或个人的需求&#xff0c;通常会使用…...

React echarts柱状图点击某个柱子跳转页面

绘制echarts柱状图 在 ECharts 中&#xff0c;如果你想要在点击柱状图的某个柱子时进行页面跳转&#xff0c;你可以通过设置 series 中的 data 属性中的 itemStyle 或者使用 series 的 label 属性中的 emphasis 属性来实现。但是&#xff0c;直接在柱状图中实现点击跳转通常涉…...

wordpress主题插件开发中高频使用的38个函数

核心模板函数 get_header()/get_footer()/get_sidebar() – 加载模板部件 the_title()/the_content()/the_excerpt() – 显示文章标题、内容、摘要 the_post() – 循环中获取文章数据 bloginfo(‘url’) – 获取站点URL wp_head()/wp_footer() – 输出头部/尾部代码 wp_n…...

ElasticSearch基础和使用

ElasticSearch基础 1 初识ES相关组件 &#xff08;1&#xff09;Elasticsearch是一款非常强大的开源搜索引擎&#xff0c;可以帮助我们从海量数据中快速找到需要的内容。Elasticsearch结合kibana、Logstash、Beats组件 也就是elastic stack&#xff08;ELK&#xff09; 广泛应…...

qt-C++笔记之QGraphicsScene和 QGraphicsView中setScene、通过scene得到view、通过view得scene

qt-C++笔记之QGraphicsScene和 QGraphicsView中setScene、通过scene得到view、通过view得scene code review! 文章目录 qt-C++笔记之QGraphicsScene和 QGraphicsView中setScene、通过scene得到view、通过view得scene1.`setScene` 方法2.通过 `scene` 获取它的视图 (`views()`)…...

小白win10安装并配置yt-dlp

需要yt-dlp和ffmpeg 注意存放路径最好都是全英文 win10安装并配置yt-dlp 一、下载1.下载yt-dlp2. fffmpeg下载 二、配置环境三、cmd操作四、yt-dlp下视频操作 一、下载 1.下载yt-dlp yt-dlp地址 找到win的压缩包点下载&#xff0c;并解压 2. fffmpeg下载 ffmpeg官方下载 …...

【kafka系列】broker

目录 Broker 接收生产者消息和返回消息给消费者的流程逻辑分析 Broker 处理生产者消息的核心流程 Broker 处理消费者消息的核心流程 关键点总结 Broker 接收生产者消息和返回消息给消费者的流程逻辑分析 Broker 处理生产者消息的核心流程 接收请求 Broker 的 SocketServer …...

用大模型学大模型05-线性回归

deepseek.com:多元线性回归的目标函数&#xff0c;损失函数&#xff0c;梯度下降 标量和矩阵形式的数学推导&#xff0c;pytorch真实能跑的代码案例以及模型,数据&#xff0c;预测结果的可视化展示&#xff0c; 模型应用场景和优缺点&#xff0c;及如何改进解决及改进方法数据推…...

Python实现AWS Fargate自动化部署系统

一、背景介绍 在现代云原生应用开发中,自动化部署是提高开发效率和保证部署质量的关键。AWS Fargate作为一项无服务器计算引擎,可以让我们专注于应用程序开发而无需管理底层基础设施。本文将详细介绍如何使用Python实现AWS Fargate的完整自动化部署流程。 © ivwdcwso (ID…...

国产编辑器EverEdit - 上下翻滚不迷路(历史编辑位置、历史光标位置回溯功能)

1 光标位置跳转 1.1 应用场景 某些场景下&#xff0c;用户从当前编辑位置跳转到别的位置查阅信息&#xff0c;如果要快速跳转回之前编辑位置&#xff0c;则可以使用光标跳转相关功能。 1.2 使用方法 1.2.1 上一个编辑位置 跳转到上一个编辑位置&#xff0c;即文本修改过的位…...

今日写题work05

题目&#xff1a;用队列实现栈 思路 队列的特点是先进先出&#xff0c;而栈的特点是后进先出。所以想要用队列实现模拟栈&#xff0c;我们可以使用两个队列&#xff0c;一个队列负责压栈&#xff0c;一个队列负责出栈。压栈很简单就是检空再调用队列的push就好&#xff0c;那出…...

[C++语法基础与基本概念] std::function与可调用对象

std::function与可调用对象 函数指针lambda表达式std::function与std::bind仿函数总结std::thread与可调用对象std::async与可调用对象回调函数 可调用对象是指那些像函数一样可以直接被调用的对象&#xff0c;他们广泛用于C的算法&#xff0c;回调&#xff0c;事件处理等机制。…...

两个实用且热门的 Python 爬虫案例,结合动态/静态网页抓取和反爬策略,附带详细代码和实现说明

在这个瞬息万变的世界里&#xff0c;保持一颗探索的心&#xff0c;永远怀揣梦想前行。即使有时会迷失方向&#xff0c;也不要忘记内心深处那盏指引你前进的明灯。它代表着你的希望、你的信念以及对未来的无限憧憬。每一个不曾起舞的日子&#xff0c;都是对生命的辜负&#xff1…...

华象新闻 | 2月20日前谨慎升级 PostgreSQL 版本

各位 PostgreSQL 用户&#xff0c;建议近期进行升级 PostgreSQL 版本。 2月20日计划进行非周期性版本发布 PostgreSQL全球开发团队计划于2025年2月20日进行一次非周期性发布&#xff0c;以解决2025年2月13日更新版本中引入的一个回归问题。 2月13日的更新版本包括了17.3、16.7、…...

跳跃游戏 II - 贪心算法解法

问题描述&#xff1a; 给定一个长度为 n 的 0 索引整数数组 nums&#xff0c;我们从数组的第一个元素 nums[0] 开始。每个元素 nums[i] 表示从索引 i 可以跳跃的最大长度&#xff0c;换句话说&#xff0c;从位置 i&#xff0c;你可以跳到位置 i j&#xff0c;其中 0 < j &…...

图像质量评价指标-UCIQE-UIQM

一、评价指标UCIQE 在文章《An underwater color image quality evaluation metric》中&#xff0c;提到的了评价指标UCIQE&#xff08;Underwater Colour Image Quality Evaluation&#xff09;&#xff0c;是一种无参考图像质量评价指标&#xff0c;主要用于评估水下图像的质…...

CentOS上安装WordPress

在CentOS上安装WordPress是一个相对直接的过程&#xff0c;可以通过多种方法完成&#xff0c;包括使用LAMP&#xff08;Linux, Apache, MySQL, PHP&#xff09;栈或使用更现代的LEMP&#xff08;Linux, Nginx, MySQL, PHP&#xff09;栈。 我选择的是&#xff08;Linux, Nginx…...

Spring Boot 原理分析

spring-boot.version&#xff1a;2.4.3.RELEASE Spring Boot 依赖管理 spring-boot-starter-parent 配置文件管理 <resources> <resource> <directory>${basedir}/src/main/resources</directory> <filtering>true&l…...

Git 本地项目上传 GitHub 全指南(SSH Token 两种上传方式详细讲解)

前言&#xff1a;Git 与 GitHub 的区别与联系 在学习如何将本地项目上传到 GitHub 之前&#xff0c;先来弄清楚 Git 和 GitHub 的区别以及它们之间的联系。 对比项GitGitHub定义分布式版本控制系统&#xff08;DVCS&#xff09;&#xff0c;用于本地和远程管理代码版本托管 G…...

RestClient

什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端&#xff0c;它允许HTTP与Elasticsearch 集群通信&#xff0c;而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级&#xff…...

多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验

一、多模态商品数据接口的技术架构 &#xff08;一&#xff09;多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如&#xff0c;当用户上传一张“蓝色连衣裙”的图片时&#xff0c;接口可自动提取图像中的颜色&#xff08;RGB值&…...

Python实现prophet 理论及参数优化

文章目录 Prophet理论及模型参数介绍Python代码完整实现prophet 添加外部数据进行模型优化 之前初步学习prophet的时候&#xff0c;写过一篇简单实现&#xff0c;后期随着对该模型的深入研究&#xff0c;本次记录涉及到prophet 的公式以及参数调优&#xff0c;从公式可以更直观…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序

一、开发环境准备 ​​工具安装​​&#xff1a; 下载安装DevEco Studio 4.0&#xff08;支持HarmonyOS 5&#xff09;配置HarmonyOS SDK 5.0确保Node.js版本≥14 ​​项目初始化​​&#xff1a; ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...

Linux云原生安全:零信任架构与机密计算

Linux云原生安全&#xff1a;零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言&#xff1a;云原生安全的范式革命 随着云原生技术的普及&#xff0c;安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测&#xff0c;到2025年&#xff0c;零信任架构将成为超…...

【C语言练习】080. 使用C语言实现简单的数据库操作

080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...

【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)

升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点&#xff0c;但无自动故障转移能力&#xff0c;Master宕机后需人工切换&#xff0c;期间消息可能无法读取。Slave仅存储数据&#xff0c;无法主动升级为Master响应请求&#xff…...

大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计

随着大语言模型&#xff08;LLM&#xff09;参数规模的增长&#xff0c;推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长&#xff0c;而KV缓存的内存消耗可能高达数十GB&#xff08;例如Llama2-7B处理100K token时需50GB内存&a…...

零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)

本期内容并不是很难&#xff0c;相信大家会学的很愉快&#xff0c;当然对于有后端基础的朋友来说&#xff0c;本期内容更加容易了解&#xff0c;当然没有基础的也别担心&#xff0c;本期内容会详细解释有关内容 本期用到的软件&#xff1a;yakit&#xff08;因为经过之前好多期…...

使用Spring AI和MCP协议构建图片搜索服务

目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式&#xff08;本地调用&#xff09; SSE模式&#xff08;远程调用&#xff09; 4. 注册工具提…...