【设计模式】【行为型模式】解释器模式(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 {
    @Override
    public 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;
    }

    @Override
    public 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;
    }

    @Override
    public 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;
    }

    @Override
    public 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;
    }

    @Override
    public 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 >= 100
        RuleExpression 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;
    }

    @Override
    public Object getValue() {
        // 直接返回字面量值(如 "42" 转换为整数)
        return this.literalValue;
    }
}

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

public class OpPlus extends Operator {
    @Override
    public 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 构建 AST
        return 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)可能过于复杂,解释器模式提供了一种轻量级的解决方案。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/970642.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

进阶数据结构——离散化

目录 一、离散化的核心思想与本质二、离散化的应用场景三、离散化的实现步骤四、离散化的复杂度分析五、离散化的优化技巧六、常见误区与调试技巧七、代码模版&#xff08;c&#xff09;八、经典例题数列离散化寻找满足高度的最大山峦美丽值 九、总结与学习建议 一、离散化的核…

VNC远程控制Mac

前言 macOS系统自带有VNC远程桌面&#xff0c;我们可以在控制端上安装配置VNC客户端&#xff0c;以此来实现远程控制macOS。但通常需要在不同网络下进行远程控制&#xff0c;为此&#xff0c;我们可以在macOS被控端上使用cpolar做内网穿透&#xff0c;映射VNC默认端口5…

[0689].第04节:Kafka与第三方的集成 – Kafka集成SpringBoot

Kafka笔记大纲 SpringBoot 是一个在 JavaEE 开发中非常常用的组件。可以用于 Kafka 的生产者&#xff0c;也可以用于 SpringBoot 的消费者 一、SpringBoot 环境准备 1.1.创建一个 Spring Initializr 1.2.引入场景启动器&#xff1a; <?xml version"1.0" encod…

「软件设计模式」装饰者模式(Decorator)

深入解析装饰者模式&#xff1a;动态扩展功能的艺术&#xff08;C实现&#xff09; 一、模式思想与应用场景 1.1 模式定义 装饰者模式&#xff08;Decorator Pattern&#xff09;是一种结构型设计模式&#xff0c;它通过将对象放入包含行为的特殊封装对象中&#xff0c;动态地…

简单几个步骤完成 Oracle 到金仓数据库(KingbaseES)的迁移目标

作为国产数据库的领军选手&#xff0c;金仓数据库&#xff08;KingbaseES&#xff09;凭借其成熟的技术架构和广泛的市场覆盖&#xff0c;在国内众多领域中扮演着至关重要的角色。无论是国家电网、金融行业&#xff0c;还是铁路、医疗等关键领域&#xff0c;金仓数据库都以其卓…

【Jenkins流水线搭建】

Jenkins流水线搭建 01、SpringBoot项目 - Jenkins基于Jar持续集成搭建文档基于手动方式发布项目基于dockerfile基于jenkins + dockerfile + jenkinsfile +pieline基于jenkins + jar方式的发布01、环境说明01、准备项目02、准备服务器03、安装git04、安装jdk1.805、安装maven依赖…

简单记录一下自己对springboot过程的理解

仅为个人简单理解&#xff0c;欢迎大家来一起讨论。 执行启动类main函数&#xff1b;读取依赖和配置文件&#xff1b;创建spring容器&#xff0c;并自动注入Bean&#xff1b;启动Tomcat。

使用 GPT-SoVITS 克隆声音,很详细

使用 GPT-SoVITS 克隆声音&#xff0c;很详细 一、前言二、下载三、启动四、克隆声音1、准备克隆音频2、分离人声伴奏3、音频分割4、语音降噪5、ASR工具6、语音文本校对标注工具7、训练模型8、微调训练9、推理 一、前言 最近对文本转语言很感兴趣&#xff0c;但对直接在网站上…

金融风控项目-业务基础

文章目录 一. 案例背景介绍二. 代码实现1. 加载数据2. 数据处理3. 查询 三. 业务解读 一. 案例背景介绍 通过对业务数据分析了解信贷业务状况 数据集说明 从开源数据改造而来&#xff0c;基本反映真实业务数据销售&#xff0c;客服可以忽略账单周期&#xff0c;放款日期账单金…

Django 创建表时 “__str__ ”方法的使用

在 Django 模型中&#xff0c;__str__ 方法是一个 Python 特殊方法&#xff08;也称为“魔术方法”&#xff09;&#xff0c;用于定义对象的字符串表示形式。它的作用是控制当对象被转换为字符串时&#xff0c;应该返回什么样的内容。 示例&#xff1a; 我在初学ModelForm时尝…

Spring Boot中如何自定义Starter

文章目录 Spring Boot中如何自定义Starter概念和作用1. 概念介绍2. 作用和优势2.1 简化依赖管理2.2 提供开箱即用的自动配置2.3 标准化和模块化开发2.4 提高开发效率2.5 提供灵活的配置覆盖3. 应用场景创建核心依赖1. 确定核心依赖的作用2. 创建 starter-core 模块2.1 依赖管理…

Python 面向对象的三大特征

前言&#xff1a;本篇讲解面向对象的三大特征&#xff08;封装&#xff0c;继承&#xff0c;多态&#xff09;&#xff0c;还有比较细致的&#xff08;类属性类方法&#xff0c;静态方法&#xff09;&#xff0c;分步骤讲解&#xff0c;比较适合理清楚三大特征的思路 面向对象的…

Windows 11 安装 Docker

1.以管理员身份打开 Windows PowerShell 2.执行下面三行命令来启动WSL和虚拟机平台 dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestartdism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norest…

CRMEB PHP多门店版v3.1.1源码全开源+PC端+Uniapp前端+搭建教程

一.介绍 CRMEB多店版是一款为品牌连锁门店打造的私域电商解决方案&#xff0c;以三大运营模式为核心&#xff0c;助力品牌连锁门店轻松构建全渠道、一体化的私域电商生态&#xff0c;促进“线上电商”与“线下门店”销售运营融合&#xff0c;加速品牌数字化转型&#xff0c;为…

Notepad++ 中删除所有以 “pdf“ 结尾的行

Notepad 中删除所有以 “pdf” 结尾的行 操作步骤 1.打开文件&#xff1a; 在 Notepad 中打开你需要处理的文本文件。 2.打开查找和替换对话框&#xff1a; 按快捷键 Ctrl F&#xff0c;打开“查找和替换”对话框。 3.启用正则表达式模式&#xff1a; 在对话框的底部&#xf…

基于SSM+uniapp的鲜花销售小程序+LW示例参考

1.项目介绍 系统角色&#xff1a;管理员、商户功能模块&#xff1a;用户管理、商户管理、鲜花分类管理、鲜花管理、订单管理、收藏管理、购物车、充值、下单等技术选型&#xff1a;SSM&#xff0c;Vue&#xff08;后端管理web&#xff09;&#xff0c;uniapp等测试环境&#x…

项目版本号生成

需求 项目想要生成一个更新版本号&#xff0c;格式为v2.0.20250101。 其中v2.0为版本号&#xff0c;更新时进行配置&#xff1b;20250101为更新日期&#xff0c;版本更新时自动生成。 实现思路 创建一个配置文件version.properties&#xff0c;在其中配置版本号&#xff1b…

Unity UI个人总结

个人总结&#xff0c;太简单的直接跳过。 一、缩放模式 1.固定像素大小 就是设置一个100x100的方框&#xff0c;在1920x1080像素下在屏幕中长度占比1/19&#xff0c;在3840x2160&#xff0c;方框在屏幕中长度占比1/38。也就是像素长款不变&#xff0c;在屏幕中占比发生变化 2.…

ArcGIS基础知识之ArcMap基础设置——ArcMap选项:常规选项卡设置及作用

作为一名 GIS 从业者,ArcMap 是我们日常工作中不可或缺的工具。对于初学者来说,掌握 ArcMap 的基础设置是迈向 GIS 分析与制图的第一步。今天,就让我们一起深入了解 ArcMap 选项中常规选项卡的各个设置,帮助大家更好地使用这款强大的软件。 在 ArcMap 中,常规选项卡是用户…

jenkins war Windows安装

Windows安装Jenkins 需求1.下载jenkins.war2.编写快速运行脚本3.启动Jenkins4.Jenkins使用 需求 1.支持在Windows下便捷运行Jenkins&#xff1b; 2.支持自定义启动参数&#xff1b; 3.有快速运行的脚步样板。 1.下载jenkins.war Jenkins下载地址&#xff1a;https://get.j…