设计模式之解释器模式的魅力:让代码读懂你的语言

目录

一、什么是解释器模式

二、解释器模式的应用场景

三、解释器模式的优缺点

3.1. 优点

3.2. 缺点

四、解释器模式示例

4.1. 问题描述

4.2. 问题分析

4.3. 代码实现

4.4. 优化方向

五、总结


一、什么是解释器模式

    解释器模式(Interpreter pattern)是一种行为型(Behavioral Pattern)的设计模式,用于定义语言的语法规则表示,并提供解释器来处理句子中的语法。该模式将句子表示为一个抽象语法树,每个节点代表一个语法规则,通过递归地解释这些节点来实现对句子的解释。

    解释器模式主要包含以下五类角色:

  • 抽象表达式(Abstract Expression):定义了解释器的接口,包括一个解释方法,并根据文法规则解释表达式。
  • 终结符表达式(Terminal Expression):实现抽象表达式接口,代表文法中的终结符。
  • 非终结符表达式(Non-Terminal Expression):实现抽象表达式接口,代表文法中的非终结符,通常包含其他表达式。
  • 上下文(Context):包含需要被解释的信息或状态,解释器通过上下文来执行解释操作。
  • 客户端(Client):创建和配置具体的解释器表达式,构建解释器的抽象语法树,并调用解释器的解释方法来解释表达式。

    解释器模式的结构如下所示:

二、解释器模式的应用场景

    解释器模式适用于需要解释和执行特定领域语言的场景,常见的适合应用解释器模式的场景包括但不限于:

  • 编程语言解释器:解释器模式最经典的应用就是编程语言的解释器。例如,Python、JavaScript等编程语言都使用解释器来解释和执行代码。
  • 数学表达式解析:解释器模式可以用于解析和计算数学表达式。例如,我们可以使用解释器模式来解析并计算一个复杂的数学公式。
  • 查询语言解析:解释器模式可以用于解析和执行查询语言。例如,数据库查询语言的解析和执行就可以使用解释器模式来实现。

三、解释器模式的优缺点

3.1. 优点

    1)可扩展性:增加新的解释表达式比较方便,扩展时不需要修改原有的逻辑,符合开闭原则。

    2)灵活性:改变或扩展文法都比较容易。

    3)实现文法容易:语法树中的每个表达式节点类都是相似的,易于实现。

3.2. 缺点

    1)类膨胀:每个语法都要产生一个非终结符表达式,可能导致大量类文件。

    2)性能问题:递归解释语法可能导致效率低下。

四、解释器模式示例

4.1. 问题描述

    为了更好地理解解释器模式的应用,我们将以一个简单的例子来演示它的实现过程。假设我们需要设计一个简单的计算器,能够解析并计算用户输入的包含加减乘除四种计算的数学表达式,其中只存在数字和加(+)、减(-)、乘(*)、除(/)四种符号,每种元素之间以空格区分,并且四种运算符号的运算顺序总是从左到右的(即不限定一定要先算乘除后算加减)。

4.2. 问题分析

    我们以一个简单的表达式“2 + 3 * 4 - 5 / 3”为例,其语法树可以表示为:

    其中包含一种终结符表达式(数字表达式)以及四种非终结符表达式(加减乘除四种表达式)。则我们可以:

    1)定义一个抽象表达式接口(AbstractExpression),并包含一个解释方法interpret()用于返回解释结果。

    2)定义一个数字表达式类(NumberExpression),它的内部维护了一个数字对象,并且其实现的interpret()方法直接返回这个数字对象。

    3)为加减乘除四种运算规则分别定义相应的表达式类(AddExpression,SubExpression,MulExpression,DivExpression),它们的内部都维护了AbstractExpression类型的运算的左值(left)和右值(right),并分别按照各自的规则实现interpret()方法返回运算结果。

    4)由于此计算器只需要从左往右顺序地计算下来就可以了,所以表达式中并不需要知道上下文环境,那么我们可以省略定义上下文类(Context),而直接定义客户端类(Client)用于对外提供运算接口(execute(String exp)),将接收到的表达式字符串参数转换成使用解释器对象描述的语法树,并解释并输出解释结果。

4.3. 代码实现

    经过上一步的分析之后,下面我们通过代码来实现它:

interface AbstractExpression{public int interpret();
}
​
class NumberExpression implements AbstractExpression{private int number;
​NumberExpression(int number) {this.number = number;}
​@Overridepublic int interpret() {return number;}
}
//加法
class AddExpression implements AbstractExpression{private AbstractExpression left;private AbstractExpression right;
​AddExpression(AbstractExpression left, AbstractExpression right) {this.left = left;this.right = right;}
​@Overridepublic int interpret() {return left.interpret() + right.interpret();}
}
//减法
class SubExpression implements AbstractExpression{private AbstractExpression left;private AbstractExpression right;
​SubExpression(AbstractExpression left, AbstractExpression right) {this.left = left;this.right = right;}
​@Overridepublic int interpret() {return left.interpret() - right.interpret();}
}
//乘法
class MulExpression implements AbstractExpression{private AbstractExpression left;private AbstractExpression right;
​MulExpression(AbstractExpression left, AbstractExpression right) {this.left = left;this.right = right;}
​@Overridepublic int interpret() {return left.interpret() * right.interpret();}
}
//除法
class DivExpression implements AbstractExpression{private AbstractExpression left;private AbstractExpression right;
​DivExpression(AbstractExpression left, AbstractExpression right) {this.left = left;this.right = right;}
​@Overridepublic int interpret() {return left.interpret() / right.interpret();}
}
​
class Cient{public int execute(String exp){String[] elements = exp.split(" ");if(elements.length % 2 == 0){throw new RuntimeException("表达式错误");}AbstractExpression expression = new NumberExpression(Integer.parseInt(elements[0]));for (int i = 1; i < elements.length-1; i+=2) {String symbol =  elements[i];int num;try{num = Integer.parseInt(elements[i+1]);}catch (NumberFormatException e){throw new RuntimeException("表达式错误");}if (symbol.equals("+")){expression = new AddExpression(expression, new NumberExpression(num));}else if (symbol.equals("-")){expression = new SubExpression(expression, new NumberExpression(num));}else if (symbol.equals("*")){expression = new MulExpression(expression, new NumberExpression(num));}else if (symbol.equals("/")){expression = new DivExpression(expression, new NumberExpression(num));}else {System.out.println("无效的符号");}}return expression.interpret();}
}

    接下来编写测试类:

    public static void main(String[] args) {Cient cient = new Cient();System.out.println(cient.execute("2 + 3 * 4 - 5 / 3"));System.out.println(cient.execute("5 * 2 + 9 - 1 / 6"));System.out.println(cient.execute("5 / 5 + 1 / 1 * 10 - 1"));}

    测试结果为:

        测试通过! 

4.4. 优化方向

    作为一个开发人员,完成一段代码功能之后当然要不断反思自己的代码还有哪些不足,就以上示例而言,针对使用者有可能的千奇百怪的输入来说,首先这段代码的异常捕获和提示内容就是不足的,当然,仅仅用来作为说明解释器模式的实例来讲,我们的目的已经达到了,所以这里不再多做补充(不是因为懒~)。另外,如果我们学习过其他设计模式,我们可以将解释器模式和其他设计模式结合使用,而达到进一步优化的目的,比如我们可以用组合模式来构建语法树,用工厂模式来创建不同类型的表达式,而如何通过代码来实现它们,就要涉及其他设计模式的说明了,本文暂不做赘述(那肯定也不是因为懒~)

五、总结

    解释器模式是一种强大而有趣的设计模式,它可以帮助我们简化复杂问题的处理过程。通过定义文法规则、创建抽象语法树和实现解释器,我们可以轻松地解释和执行特定语言的句子。希望本文对你理解解释器模式有所帮助,能够在实际开发中灵活运用!

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring: 在SpringBoot项目中解决前端跨域问题

这里写目录标题 一、什么是跨域问题二、浏览器的同源策略三、SpringBoot项目中解决跨域问题的5种方式&#xff1a;使用CORS1、自定 web filter 实现跨域(全局跨域)2、重写 WebMvcConfigurer(全局跨域)3、 CorsFilter(全局跨域)4、使用CrossOrigin注解 (局部跨域) 一、什么是跨域…

文件操作(顺序读写篇)

1. 顺序读写函数一览 函数名功能适用于fgetc字符输入函数所有输入流fputc字符输出函数所有输出流fgets文本行输入函数所有输入流fputs文本行输出函数所有输出流fscanf格式化输入函数所有输入流fprintf格式化输出函数所有输出流fread二进制输入文件fwrite二进制输出文件 上面说…

时序预测 | Matlab实现GWO-BP灰狼算法优化BP神经网络时间序列预测

时序预测 | Matlab实现GWO-BP灰狼算法优化BP神经网络时间序列预测 目录 时序预测 | Matlab实现GWO-BP灰狼算法优化BP神经网络时间序列预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.Matlab实现GWO-BP灰狼算法优化BP神经网络时间序列预测&#xff08;完整源码和数据…

如何在OceanBase的OCP多节点上获取日志

背景 在使用OceanBase的OCP的过程中&#xff0c;因各种因素&#xff0c;我们可能需要对当前页面进行跟踪。在单一ocp节点环境下&#xff0c;我们自然可以直接在该节点上查找所需的日志。然而&#xff0c;当我们的环境中部署了多个ocp节点时&#xff0c;在排查问题时就会变得相…

WPF中获取TreeView以及ListView获取其本身滚动条进行滚动

实现自行调节scoll滚动的位置(可相应获取任何控件中的内部滚动条) TreeView:TreeViewAutomationPeer lvap new TreeViewAutomationPeer(treeView); var svap lvap.GetPattern(PatternInterface.Scroll) as ScrollViewerAutomationPeer; var scroll svap.Owner as ScrollVie…

sql Tuning Advisor启用导致业务性能问题

数据库每天晚上10点后业务性能很卡&#xff0c;大量的insert被堵塞&#xff0c;查询等待事件发现有大量的“library cache lock”和“cursor: pin S wait on X”。 22:00数据库的统计信息开始收集&#xff0c; Sql Tuning Advisor堵塞了统计信息的收集&#xff0c;等待事件是“…

Python之Opencv教程(1):读取图片、图片灰度处理

1、Opencv简介 OpenCV&#xff08;Open Source Computer Vision Library&#xff09;是一个用于计算机视觉和图像处理的开源库&#xff0c;提供了丰富的图像处理、计算机视觉和机器学习功能。它支持多种编程语言&#xff0c;包括C、Python、Java等&#xff0c;广泛应用于图像处…

Redis 的慢日志

Redis 的慢日志 Redis 的慢日志&#xff08;Slow Log&#xff09;是用于记录执行时间超过预设阈值的命令请求的系统。慢日志可以帮助运维人员和开发人员识别潜在的性能瓶颈&#xff0c;定位那些可能导致 Redis 性能下降或响应延迟的慢查询。以下是 Redis 慢日志的相关细节&…

Linux IRC

目录 入侵框架检测 检测流程图 账号安全 查找账号中的危险信息 查看保存的历史命令 检测异常端口 入侵框架检测 1、系统安全检查&#xff08;进程、开放端口、连接、日志&#xff09; 这一块是目前个人该脚本所实现的功能 2、Rootkit 建议使用rootkit专杀工具来检查&#…

【算法-PID】

算法-PID ■ PID■ 闭环原理■ PID 控制流程■ PID 比例环节&#xff08;Proportion&#xff09;■ PID 积分环节&#xff08;Integral&#xff09;■ PID 微分环节&#xff08;Differential&#xff09; ■ 位置式PID&#xff0c;增量式PID介绍■ 位置式 PID 公式■ 增量式 PI…

嵌入式数据库-Sqlite3

阅读引言&#xff1a; 本文将会从环境sqlite3的安装、数据库的基础知识、sqlite3命令、以及sqlite的sql语句最后还有一个完整的代码实例&#xff0c; 相信仔细学习完这篇内容之后大家一定能有所收获。 目录 一、数据库的基础知识 1.数据库的基本概念 2.常用数据库 3.嵌入式…

wordpress插件,免费的wordpress插件

WordPress作为世界上最受欢迎的内容管理系统之一&#xff0c;拥有庞大的插件生态系统&#xff0c;为用户提供了丰富的功能扩展。在内容创作和SEO优化方面&#xff0c;有一类特殊的插件是自动生成原创文章并自动发布到WordPress站点的工具。这些插件能够帮助用户节省时间和精力&…

Hides for Mac:应用程序隐藏工具

Hides for Mac是一款功能强大的应用程序隐藏工具&#xff0c;专为Mac用户设计。它能够帮助用户快速隐藏当前正在运行的应用程序窗口&#xff0c;保护用户的隐私和工作内容&#xff0c;避免不必要的干扰。 软件下载&#xff1a;Hides for Mac下载 Hides for Mac的使用非常简单直…

初步了解C++

目录 一&#xff1a;什么是C&#xff1f; 二.C发展史 三:C关键字 四&#xff1a;命名空间 4.1命名空间的介绍 4.2命名空间的使用 4.3命名空间的使用 4.3.1使用作用域限定符 4.3.2 使用using将命名空间的某个成员引入 4.3.3使用using把整个命名空间展开 4.4命名空…

BaseDao封装增删改查

文章目录 什么是BaseDao操作代码增删改查询单个数据查询多个数据 总结 什么是BaseDao BaseDao是&#xff1a; 数据库里负责增加&#xff0c;删除&#xff0c;修改&#xff0c;查询 具体来说是一种接口代码,公共方法的接口类。 在dao层新建basedao,其他dao层接口继承basedao 相…

BC40056 Imports“SolidWorks.Interop.swconst”中指定的命名空间或类型不包含任何公共成员

BC40056 Imports“SolidWorks.Interop.swconst”中指定的命名空间或类型不包含任何公共成员&#xff0c;或者找不到该命名空间或类型。 问题描述原因分析 解决办法 ) 问题描述 严重性 代码 说明 项目 文件 行 警告 BC40056 Imports“SolidWorks.Interop.swconst”中指定的命名…

基于SSM框架的校园失物招领系统:从设计思路到实现细节

末尾获取源码作者介绍&#xff1a;大家好&#xff0c;我是墨韵&#xff0c;本人4年开发经验&#xff0c;专注定制项目开发 更多项目&#xff1a;CSDN主页YAML墨韵 学如逆水行舟&#xff0c;不进则退。学习如赶路&#xff0c;不能慢一步。 目录 一、项目简介 二、开发技术与环…

Java代码基础算法练习-自定义函数之字符串连接-2024.03.30

任务描述&#xff1a; 写一函数&#xff0c;将两个字符串连接起来&#xff0c;然后在主函数中调用该函数实现字符串连接操作。 任务要求&#xff1a; 代码示例&#xff1a; package M0317_0331;import java.util.Scanner;public class m240330 {public static void main(Stri…

Python 妙用运算符重载——玩出“点”花样来

目录 运算符重载 主角点类 魔法方法 __getitem__ __setitem__ __iter__ __next__ __len__ __neg__ __pos__ __abs__ __bool__ __call__ 重载运算符 比较运算符 相等 不等 ! 大于和小于 >、< 大于等于和小于等于 >、< 位运算符 位与 & 位…

腾讯 tengine 替代 nginx

下载地址 变更列表 - The Tengine Web Server 解压 tar -xvf 安装包.gz 进入到解压目录 cd 解压目录 使用 ./configure 命令来指定安装目录,这边指定安装到 /opt/tengine/install路径下 新建install目录 ./configure --prefix/opt/tengine/install 检查是否有缺失的依…