解释器模式原理剖析和Spring中的应用

解释器模式原理剖析和Spring中的应用

解释器模式 是一种行为型设计模式,它定义了一种语言的文法表示,并提供了一个解释器来处理该文法的表达式。解释器模式可以用于构建语法解释器,例如计算器、简单编程语言的解释器等。

核心思想

解释器模式可以根据语言的文法定义多个表达式类,这些类通过组合和解释可以解析复杂的语言结构。它特别适合用于处理复杂表达式规则引擎,如数学表达式计算、正则表达式解析、脚本语言解释器等。

解释器模式的 UML 类图

在这里插入图片描述

角色说明:

  1. Expression(抽象表达式)
    • 抽象类或接口,定义了解释表达式的方法 interpret(),所有具体表达式类都实现该接口。
  2. TerminalExpression(终结符表达式)
    • 具体表达式类,用于解释最小的语法单元,如常量或变量。
  3. NonTerminalExpression(非终结符表达式,如 OrExpression、AndExpression)
    • 复杂表达式类,用于组合其他表达式,并定义复杂的解释操作(如逻辑运算、算术运算等)。

生动案例:简单规则引擎

场景描述

假设我们要设计一个规则引擎,用于处理用户输入的条件。条件表达式由多个规则组成,并且支持**“与”(AND)“或”(OR)**操作。具体规则包括判断输入的字符串是否包含某个单词。

例如:

  • 用户输入:“apple and orange”
  • 规则表达式:“包含’apple’ AND 包含’orange’”

通过解释器模式,我们可以设计一个系统来解析和执行这些规则。

代码实现:简单规则引擎

Step 1: 定义表达式接口

Expression 接口定义了 interpret() 方法,所有具体的表达式类都将实现该方法。

// 抽象表达式接口
public interface Expression {
    boolean interpret(String context);
}

Step 2: 实现终结符表达式

TerminalExpression 负责解释最基本的表达式,它判断一个字符串中是否包含指定的单词。

// 终结符表达式:判断字符串中是否包含某个单词
public class TerminalExpression implements Expression {
    private String word;

    public TerminalExpression(String word) {
        this.word = word;
    }

    @Override
    public boolean interpret(String context) {
        return context.contains(word);
    }
}

Step 3: 实现非终结符表达式

非终结符表达式 OrExpressionAndExpression 组合多个子表达式,用于处理更复杂的逻辑表达式。

OrExpression

// 非终结符表达式:OR 操作
public class OrExpression implements Expression {
    private Expression expr1;
    private Expression expr2;

    public OrExpression(Expression expr1, Expression expr2) {
        this.expr1 = expr1;
        this.expr2 = expr2;
    }

    @Override
    public boolean interpret(String context) {
        return expr1.interpret(context) || expr2.interpret(context);
    }
}

AndExpression

// 非终结符表达式:AND 操作
public class AndExpression implements Expression {
    private Expression expr1;
    private Expression expr2;

    public AndExpression(Expression expr1, Expression expr2) {
        this.expr1 = expr1;
        this.expr2 = expr2;
    }

    @Override
    public boolean interpret(String context) {
        return expr1.interpret(context) && expr2.interpret(context);
    }
}

Step 4: 测试解释器模式

创建一些表达式并测试它们的解析逻辑。

public class InterpreterPatternDemo {

    // 构建规则:"apple OR orange"
    public static Expression getOrExpression() {
        Expression apple = new TerminalExpression("apple");
        Expression orange = new TerminalExpression("orange");
        return new OrExpression(apple, orange);
    }

    // 构建规则:"apple AND orange"
    public static Expression getAndExpression() {
        Expression apple = new TerminalExpression("apple");
        Expression orange = new TerminalExpression("orange");
        return new AndExpression(apple, orange);
    }

    public static void main(String[] args) {
        Expression orExpression = getOrExpression();
        Expression andExpression = getAndExpression();

        String test1 = "I like apple";
        String test2 = "I like orange";
        String test3 = "I like apple and orange";
        String test4 = "I like banana";

        System.out.println("Test1 (apple OR orange): " + orExpression.interpret(test1));
        System.out.println("Test2 (apple OR orange): " + orExpression.interpret(test2));
        System.out.println("Test3 (apple AND orange): " + andExpression.interpret(test3));
        System.out.println("Test4 (apple AND orange): " + andExpression.interpret(test4));
    }
}

输出结果

Test1 (apple OR orange): true
Test2 (apple OR orange): true
Test3 (apple AND orange): true
Test4 (apple AND orange): false

解释器模式在 Spring 框架中的应用剖析

解释器模式 在 Spring 框架中的应用并不像某些行为型模式(如策略模式、工厂模式)那样直接、明显,然而在一些高级功能或组件的实现中,解释器模式的思想仍然被应用到了。尤其是在需要处理表达式、规则解析或动态解析配置的场景中。

以下是解释器模式在 Spring 框架 中的几个重要应用场景:

1. Spring Expression Language (SpEL)

场景描述:

Spring 提供了一种强大的表达式语言——SpEL,它可以在运行时解析和求值表达式。这种语言的设计和实现思想与解释器模式非常类似。通过解释器模式,Spring 能够解析复杂的表达式,支持调用方法、访问对象属性、执行逻辑运算等功能。

SpEL 使用示例:
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

public class SpELDemo {

    public static void main(String[] args) {
        ExpressionParser parser = new SpelExpressionParser();

        // 解析并求值表达式
        Expression exp = parser.parseExpression("'Hello' + ' World'");
        String message = exp.getValue(String.class);
        System.out.println(message); // 输出:Hello World

        // 访问对象属性
        Person person = new Person("John", 30);
        StandardEvaluationContext context = new StandardEvaluationContext(person);
        String name = parser.parseExpression("name").getValue(context, String.class);
        System.out.println(name); // 输出:John
    }

    static class Person {
        public String name;
        public int age;

        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
    }
}

解释

  • SpEL 是典型的解释器模式:用户可以定义表达式,而 SpEL 解析器会根据表达式的结构生成相应的解释器并执行它。这就像是解释器模式中的 TerminalExpressionNonTerminalExpression,SpEL 可以根据表达式中的操作符和操作数构建语法树,并对其进行解析和求值。
SpEL 工作原理:
  1. 解析表达式ExpressionParser 负责解析表达式字符串,生成相应的表达式对象。
  2. 生成解释器:解析器通过生成解释器类(类似于解释器模式中的 Expression),该类实现对表达式中每个操作符的解释。
  3. 求值:表达式对象通过调用 getValue() 方法执行解析和求值操作。
SpEL 的架构角色:
  • ExpressionParser:充当解释器模式中的 Context,负责管理表达式的解析和执行。
  • Expression:是解释器模式中的 AbstractExpression,负责定义表达式的抽象求值接口。
  • StandardEvaluationContext:类似于上下文环境,提供解析表达式时的环境变量。

2. @Value 注解中的表达式

在 Spring 中,使用 @Value 注解时可以结合 SpEL 表达式,动态解析 Bean 的属性值。这里用到的 SpEL 解析过程背后也是基于解释器模式。

示例

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class ConfigBean {

    @Value("#{2 + 3}")
    private int sum;

    @Value("#{configBean.sum * 2}")
    private int doubleSum;

    public int getSum() {
        return sum;
    }

    public int getDoubleSum() {
        return doubleSum;
    }
}

解释

  • @Value 注解中的表达式是在运行时解析的,它利用 SpEL 表达式解释器动态计算值。这些表达式解析的背后是解释器模式,Spring 在启动时解析表达式,生成解释器实例并求值。
  • 该模式允许开发者使用表达式来计算 Bean 属性值、访问其他 Bean 的属性或方法,并动态地将其注入到当前 Bean 中。

3.Spring AOP (Aspect-Oriented Programming)

Spring 的 **AOP(面向切面编程)**虽然不是直接使用解释器模式,但它在动态代理和切点表达式的解析过程中,采用了类似于解释器模式的机制。

AOP 中的表达式解析

  • 在定义切面时,开发者可以使用表达式来定义切点(@Pointcut)。Spring 在运行时会解析这些表达式,决定在哪些地方应用切面逻辑。

示例

@Aspect
@Component
public class LoggingAspect {

    @Pointcut("execution(* com.example.service.*.*(..))")
    public void serviceLayer() {}

    @Before("serviceLayer()")
    public void logBefore(JoinPoint joinPoint) {
        System.out.println("Executing method: " + joinPoint.getSignature());
    }
}

解释

  • @Pointcut 中的 execution 表达式在运行时由 Spring AOP 框架解析,决定哪些方法会被拦截。
  • Spring AOP 背后采用了类似于解释器模式的机制来解析切点表达式,并动态应用横切关注点。

AOP 中的角色映射

  • @Pointcut 表达式:作为抽象表达式,描述了要执行的切面逻辑。
  • Spring AOP 框架:类似于解释器模式中的上下文,负责解析和执行切面表达式。

4.Spring 中的 EL 表达式

Spring 的配置文件中支持使用 表达式语言(EL),例如可以在 XML 配置中通过 SpEL 表达式动态计算或引用属性

<bean id="exampleBean" class="com.example.MyBean">
    <property name="sum" value="#{2 + 3}" />
</bean>

解释

  • Spring 通过解释器模式动态解析表达式并计算出结果。在配置文件加载时,Spring 会调用 SpEL 解释器,解析表达式并将结果注入到 Bean 属性中。

总结

  1. **扩展性强:**可以通过增加新的表达式类来扩展解释器,适合构建可扩展的文法解析系统。
  2. **易于理解和调试:**由于每个表达式类只负责一个小的功能,解释器模式的结构非常清晰,易于理解和测试。
  3. 性能问题:解释器模式在处理复杂的表达式时,可能会产生大量的对象,增加内存开销,并且计算过程可能较慢。
  4. 类的数量增加:对于每种语法规则或操作符,都会有一个对应的表达式类,可能导致类的数量急剧增加。

应用场景

  1. 数学表达式求值
    • 解释器模式非常适合用于实现数学表达式求值,如计算器的四则运算。
  2. 规则引擎
    • 解释器模式可用于构建规则引擎,帮助解析并执行复杂的业务规则。
  3. 脚本语言解释器
    • 解释器模式可以用于构建简单的脚本语言解释器,帮助解析并执行用户输入的命令或脚本。
  4. 正则表达式解析
    • 解释器模式可以应用于正则表达式的解析和匹配。

解释器模式 提供了一种灵活的方式来处理复杂的语法结构和表达式。通过定义终结符和非终结符表达式,解释器模式可以轻松地解析和计算复杂的表达式,如我们在例子中实现的四则运算解释器。通过递归组合表达式,系统能够处理复杂的逻辑和运算。

在现实世界中,解释器模式常用于数学表达式求值规则引擎脚本语言解释器等场景,它能让代码更具扩展性,并且容易理解和维护。

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

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

相关文章

Java框架学习(mybatis)(01)

简介&#xff1a;以本片记录在尚硅谷学习ssm-mybatis时遇到的小知识 详情移步&#xff1a;想参考的朋友建议全部打开相互配合学习&#xff01; 官方文档&#xff1a; MyBatis中文网https://mybatis.net.cn/index.html 学习视频&#xff1a; 067-mybatis-介绍和对比_哔哩哔…

人工智能时代,程序员如何保持核心竞争力?

引言 随着AIGC&#xff08;如ChatGPT、Midjourney、Claude等&#xff09;大语言模型接二连三的涌现&#xff0c;AI辅助编程工具日益普及&#xff0c;程序员的工作方式正在发生深刻变革。有人担心AI可能取代部分编程工作&#xff0c;也有人认为AI是提高效率的得力助手。面对这一…

一天面了8个Java后端,他们竟然还在背5年前的八股文!

今天面了8个Java候选人&#xff0c;在面试中我发现他们还停留在面试背八股文的阶段&#xff0c;5年前面试背八股文没问题&#xff0c;随着市场竞争越来越激烈&#xff0c;再问普通的Java八股文已经没有意义了&#xff0c;因为考察不出来获选人的真实实力&#xff01; 现在面试…

cpu路、核、线程、主频、缓存

路&#xff1a;主板插口实际插入的 CPU 个数&#xff0c;也可以理解为主板上支持的CPU的数量。每个CPU插槽可以插入一个物理处理器芯片。例如&#xff0c;一台服务器可能有2路或4路插槽&#xff0c;这意味着它最多可以安装2个或4个物理处理器。 核&#xff1a;单块 CPU 上面能…

C++ 异步编程中:future与promise、packaged_task、async

原文链接&#xff1a;C 异步编程之future与promise、async、packaged_task_std::promise和std::future异步发送-CSDN博客 1、std::future std::future类模板来关联线程运行的函数和函数的返回结果&#xff0c;这种获取结果的方式是异步的 std::future 通常由某个 Provider 创建…

unity 打包安卓 RenderTexture显示红色

1、ColorFarmat&#xff1a; 每个图形卡可能并不支持跨格式的所有用法。使用 SystemInfo.IsFormatSupported 可以检查图形卡支持的用法。 None未指定格式。R8G8B8A8_UNorm一种四分量、32 位无符号归一化格式&#xff0c;在字节 0 中具有 8 位 R 分量&#xff0c;在字节 1 中具…

Java面试篇基础部分-ReentrantLock详解(二)

Lock 接口的主要方法 void lock():给对象加锁,如果锁没有被其他线程使用,则当前线程获取到这个锁;如果锁正在被其他线程持有,则将禁用当前线程,直到当前线程获取到锁。boolean tryLock():试图给对象进行加锁操作,如果锁没有被其他线程使用,则将获取到这个锁并且返回tr…

Linux ubuntu debian系统安装UFW防火墙图形化工具GUFW

GUFW是UFW的图形化前端&#xff0c;可以通过以下命令安装&#xff1a; sudo apt install gufw安装成功后&#xff0c;可以通过应用程序菜单启动GUFW&#xff0c;在图形界面中&#xff0c;可以方便地添加、修改和删除规则&#xff0c;查看状态和日志。

【入门01】arcgis api 4.x 创建地图、添加图层、添加指北针、比例尺、图例、卷帘、图层控制、家控件(附完整源码)

1.效果 2.代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title></title><link rel"s…

CDGA|数据治理:策略与价值的深度融合

在当今这个数据驱动的时代&#xff0c;企业数据治理的重要性日益凸显。数据不仅是企业的核心资产&#xff0c;更是驱动业务决策、优化运营流程、创新产品服务的关键力量。然而&#xff0c;要让数据治理真正发挥价值&#xff0c;企业需要采取一系列策略来确保数据的准确性、完整…

C++之二叉搜索

1.二叉搜索树的概念 二叉搜索树又称为二叉排序树&#xff0c;它有以下的特点。 1.如果它的左子树不为空&#xff0c;则左子树上所以结点的值都小于等于根结点的值 2.如果它的右子树不为空&#xff0c;则右子树上所有结点都大于等于根结点的值 3.它的左右子树也分别为二叉搜…

【C++笔试强训】如何成为算法糕手Day3

​ 学习编程就得循环渐进&#xff0c;扎实基础&#xff0c;勿在浮沙筑高台 循环渐进Forward-CSDN博客 目录 循环渐进Forward-CSDN博客 第一题&#xff1a;除2&#xff01; 第二题&#xff1a;dd爱框框 第三题&#xff1a;简写单词 第一题&#xff1a;除2&#xff01; 牛客网…

数据保护从现在开始:如何抵御 .[RestoreBackup@cock.li].SRC 勒索病毒

导言 勒索病毒是一种不断演变的网络威胁&#xff0c;.[RestoreBackupcock.li].SRC、[chewbaccacock.li].SRC勒索病毒便是其中一种新型的攻击手段。该病毒通过加密用户文件并要求支付赎金来恢复访问&#xff0c;给个人和企业带来了严重的安全风险和经济损失。本文91数据恢复将探…

25 基于51单片机的温度电流电压检测系统(压力、电压、温度、电流、LCD1602)

目录 一、主要功能 二、硬件资源 三、程序编程 四、实现现象 一、主要功能 基于51单片机&#xff0c;通过DS18B20检测温度&#xff0c;滑动变阻器连接数模转换器模拟电流、电压&#xff0c;通过LCD1602显示&#xff0c;程序里设置温度阈值为40&#xff0c;电流阈值为60&am…

新版torch_geometric不存在uniform、maybe_num_nodes函数问题(Prune4ED论文报错解决)

这是在复现论文“Towards accurate subgraph similarity computation via neural graph pruning”时遇到的报错。 ImportError: cannot import name uniform from torch_geometric.nn.pool.topk_pool 一、报错原因 论文作者使用的是2.1.0版本的torch_geometric。而我安装了2.…

[vulnhub] Jarbas-Jenkins

靶机链接 https://www.vulnhub.com/entry/jarbas-1,232/ 主机发现端口扫描 扫描网段存活主机&#xff0c;因为主机是我最后添加的&#xff0c;所以靶机地址是135的 nmap -sP 192.168.75.0/24 // Starting Nmap 7.93 ( https://nmap.org ) at 2024-09-21 14:03 CST Nmap scan…

linux信号| 学习信号三步走 | 学习信号需要打通哪些知识脉络?

前言: 本节内容主要讲解linux下信号的预备知识以及信号的概念&#xff0c; 信号部分我们将会分为几个阶段进行讲解&#xff1a;信号的概念&#xff0c; 信号的产生&#xff0c; 信号的保存。本节主要讲解信号 ps:本节内容适合学习了进程相关概念的友友们进行观看哦 目录 什么是…

教练车一键启动应用‌案例

教练车一键启动应用‌主要提供了便捷的车辆启动方式&#xff0c;通过一个按钮实现车辆的启动和熄火&#xff0c;简化了传统的打火过程。这种智能配置不仅提升了车辆的科技感&#xff0c;还增加了市场竞争力。一键启动系统可以在原车钥匙锁头位置安装&#xff0c;也可以作为独立…

基于JAVA+SpringBoot+Vue的疫苗发布和接种预约系统

基于JAVASpringBootVue的疫苗发布和接种预约系统 前言 ✌全网粉丝20W,csdn特邀作者、博客专家、CSDN[新星计划]导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末附源码下载链接&#x1f3…

3D 模型GLTF、GLB格式文件介绍使用;FBX格式

一、GLTF、GLB介绍 GLTF&#xff08;GL Transmission Format&#xff09;和 GLB&#xff08;GL Binary&#xff09;是用于在 Web 和各种应用程序中传输和加载 3D 场景和模型的开放标准格式。它们由 Khronos Group 开发&#xff0c;旨在提供一种高效、可扩展且易于使用的 3D 内…