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

Java 设计模式——解释器模式

目录

  • 1.概述
  • 2.结构
  • 3.案例实现
    • 3.1.抽象表达式类
    • 3.2.终结表达式
    • 3.3.非终结表达式
    • 3.4.环境类
    • 3.5.测试
  • 4.优缺点
  • 5.使用场景

1.概述

(1)如下图,设计一个软件用来进行加减计算。我们第一想法可能就是使用工具类,提供对应的加法和减法的工具方法。

在这里插入图片描述

//用于两个整数相加 
public static int add(int a,int b){ return a + b; 
}//用于两个整数相加 
public static int add(int a,int b,int c){ return a + b + c;
}//用于 n 个整数相加(使用可变参数)
public static int add(Integer... arr) { int sum = 0; for (Integer i : arr) { sum += i; }return sum; 
}

上面的形式比较单一、有限,如果形式变化非常多,这就不符合要求,因为加法和减法运算,两个运算符与数值可以有无限种组合方式。比如 1 + 2 + 3 + 4 + 5、1 + 2 + 3 - 4等等。显然,现在需要一种翻译识别机器,能够解析由数字以及 +、- 符号构成的合法的运算序列。如果把运算符和数字都看作节点的话,能够逐个节点的进行读取解析运算,这就是解释器模式的思维

(2)解释器模式 (Interpreter pattern) 是一种行为型设计模式,用于表示一个语言文法的语法规则,以及如何解析和执行该语法规则。解释器模式将一个语言表达式表示为对象,并定义了解析该表达式的语法规则。它包含了一个抽象表达式类和具体的表达式类。解释器模式主要通过使用递归来实现计算过程,即通过调用表达式对象的解释方法来进行计算。它可以用于编译器、解释器、数学公式计算等领域。

(3)在解释器模式中,我们需要将待解决的问题,提取出规则,抽象为一种“语言”。比如加减法运算,规则为:由数值和 +、- 符号组成的合法序列,“1 + 3 - 2” 就是这种语言的句子。解释器就是要解析出来语句的含义。但是如何描述规则呢?

(4)文法/语法规则(用于描述语言的语法结构的形式规则):

expression ::= value | plus | minus 
plus ::= expression '+' expression 
minus ::= expression '-' expression 
value ::= integer

注意: 这里的符号“::=”表示“定义为”的意思,竖线 | 表示或,左右的其中一个,引号内为字符本身,引号外为语法。上面规则描述为 :表达式可以是一个值,也可以是 plus 或者 minus 运算,而 plus 和 minus 又是由表达式结合运算符构成,值的类型为整型数。

(5)抽象语法树
在计算机科学中,抽象语法树 (Abstract Syntax Tree,AST),或简称语法树 (Syntax tree),是源代码语法结构的一种抽象表示。它以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构。用树形来表示符合文法规则的句子:
在这里插入图片描述

2.结构

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

  • 抽象表达式 (Abstract Expression) 角色:定义了一个抽象的接口,用于解析语法规则。通常它是一个抽象类或接口,其中包含了解释器需要实现的方法。。
  • 终结符表达式 (Terminal Expression) 角色:终结符表达式是最基本的表达式,它代表语言中的一个基本单元,通常是一个具体的值或一个变量。
  • 非终结符表达式 (Nonterminal Expression) 角色:非终结符表达式是由多个终结符表达式或其他非终结符表达式组成的复合表达式。
  • 环境 (Context) 角色:环境对象包含了解析器运行所需要的全局信息。
  • 客户端 (Client) 角色:客户端用于创建解释器并调用其解释方法来解析语法规则。

3.案例实现

【例】设计实现加减法的软件
在这里插入图片描述
具体实现代码如下:

3.1.抽象表达式类

AbstractExpression.java

//抽象表达式类
public abstract class AbstractExpression {public abstract int interpret(Context context);
}

3.2.终结表达式

Variable.java

//用于封装变量的类
public class Variable extends AbstractExpression{//声明存储变量名的成员变量private String name;public Variable(String name) {this.name = name;}@Overridepublic int interpret(Context context) {//直接返回变量的值return context.getValue(this);}@Overridepublic String toString() {return name;}
}

3.3.非终结表达式

Plus.java

//加法表达式类
public class Plus extends AbstractExpression{// + 左边的表达式private AbstractExpression left;// + 右边的表达式private AbstractExpression right;public Plus(AbstractExpression left, AbstractExpression right) {this.left = left;this.right = right;}@Overridepublic int interpret(Context context) {//将左边表达式的结果和右边的进行相加return left.interpret(context) + right.interpret(context);}@Overridepublic String toString() {return "(" + left.toString() + "+" + right.toString() + ")";}
}

Minus.java

//减法表达式类
public class Minus extends AbstractExpression{// - 左边的表达式private AbstractExpression left;// - 右边的表达式private AbstractExpression right;public Minus(AbstractExpression left, AbstractExpression right) {this.left = left;this.right = right;}@Overridepublic int interpret(Context context) {//将左边表达式的结果和右边的进行相减return left.interpret(context) - right.interpret(context);}@Overridepublic String toString() {return "(" + left.toString() + "-" + right.toString() + ")";}
}

3.4.环境类

Context.java

//环境角色类
public class Context {//定义一个 map 集合,用来存储变量以及对应的值private Map<Variable,Integer> map = new HashMap<>();//添加变量的功能public void assign(Variable var, Integer value){map.put(var, value);}//根据变量获取对应的值public int getValue(Variable var){return map.get(var);}
}

3.5.测试

Client.java

public class Client {public static void main(String[] args){//创建环境对象Context context = new Context();//创建多个变量对象Variable a = new Variable("a");Variable b = new Variable("b");Variable c = new Variable("c");Variable d = new Variable("d");//将变量存储到环境变量中context.assign(a, 1);context.assign(b, 2);context.assign(c, 3);context.assign(d, 4);//获取抽象语法树  a + b - c + dAbstractExpression expression = new Minus(a, new Plus(new Minus(b, c), d));//解释,即计算int result = expression.interpret(context);System.out.println(expression + "=" + result);}
}

结果如下:

(a-((b-c)+d))=-2

4.优缺点

(1)解释器模式的主要优点是:

  • 灵活性:解释器模式可以根据需要动态地修改解释器的表达式,从而扩展或修改语言。
  • 易于扩展:新的解释器可以通过扩展抽象语法树节点类来轻松添加到系统中。
  • 可重用性:同样的解释器可以在不同的环境下重用,只需要修改解释器的上下文即可。
  • 独立性:解释器模式使得解释器与其它部分相互独立,修改解释器不会影响其他部分的功能。

(2)解释器模式的主要缺点是:

  • 复杂性:因为解释器模式涉及到抽象语法树、终结符和非终结符等概念,因此实现起来比较复杂。
  • 效率问题:由于解释器模式使用递归调用的方式进行解释处理,因此对于复杂的语法和大量的数据处理,可能会导致效率问题。

5.使用场景

(1)解释器模式适用于以下场景:

  • 当有一个简单的语法规则,并且需要频繁地对该语法进行解释和执行时,可以考虑使用解释器模式。
  • 当需要将一个语言或规则进行扩展或修改时,解释器模式可以提供灵活的扩展性和易于修改的特性。
  • 当语法规则相对稳定,但需要根据不同的上下文进行不同的解释时,可以使用解释器模式。
  • 当需要解耦语法规则的解释过程与具体的操作时,解释器模式可以将语法解释与操作分离,从而提高代码的可维护性和可扩展性。
  • 当需要构建一个能够解释执行特定领域语言的工具或引擎时,解释器模式是一种常用的设计模式。

(2)总之,解释器模式适用于需要解释和执行简单语法规则、动态扩展语言等场景,可以提供灵活性、可扩展性和可维护性。

相关文章:

Java 设计模式——解释器模式

目录 1.概述2.结构3.案例实现3.1.抽象表达式类3.2.终结表达式3.3.非终结表达式3.4.环境类3.5.测试 4.优缺点5.使用场景 1.概述 &#xff08;1&#xff09;如下图&#xff0c;设计一个软件用来进行加减计算。我们第一想法可能就是使用工具类&#xff0c;提供对应的加法和减法的…...

面试经典150题——Day37

文章目录 一、题目二、题解 一、题目 73. Set Matrix Zeroes Given an m x n integer matrix matrix, if an element is 0, set its entire row and column to 0’s. You must do it in place. Example 1: Input: matrix [[1,1,1],[1,0,1],[1,1,1]] Output: [[1,0,1],[0,…...

在 Arduino IDE 2.0 中安装 ESP32 板(Windows、Mac OS X、Linux)

有一个新的 Arduino IDE——Arduino IDE 2.0&#xff08;测试版&#xff09;。在本教程中&#xff0c;您将学习如何在 Arduino IDE 2.0 中安装 ESP32 板并将代码上传到板。本教程与 Windows、Mac OS X 和 Linux 操作系统兼容。 据 Arduino 网站称&#xff1a;“ Arduino IDE 2.…...

西门子S7-1200PLC混合通信编程(ModbusTcp和UDP通信)

S7-1200PLC的MODBUS-TCP通信 西门子PLC ModbusTcp通信访问网关后从站(SCL语言轮询状态机)-CSDN博客文章浏览阅读305次。西门子PLC的ModbusTcp通信在专栏已有很多文章介绍,所不同的是每个项目的通信需求都略有不同,今天我们以访问网关后的三个从站数据来举例,给出轮询的推荐…...

Hbase 迁移小结:从实践中总结出的最佳迁移策略

在数据存储和处理领域&#xff0c;HBase作为一种分布式、可扩展的NoSQL数据库&#xff0c;被广泛应用于大规模数据的存储和分析。然而&#xff0c;随着业务需求的变化和技术发展的进步&#xff0c;有时候我们需要将现有的HBase数据迁移到其他环境或存储系统。HBase数据迁移是一…...

键盘win键无法使用,win+r不生效、win键没反应、Windows键失灵解决方案(亲测可以解决)

最近几天发现自己笔记本的win键无法使用&#xff0c;win失灵了&#xff0c;但是外接键盘后则正常:。 这个问题困扰了我一周&#xff0c;我都以为自己的枪神坏了。 寻找了几个解决方法&#xff0c;网上看了好多好多稀里糊涂的办法&#xff0c;都是不管用的&#xff0c;这里给大…...

1. 深度学习——激活函数

机器学习面试题汇总与解析——激活函数 本章讲解知识点 什么是激活函数&#xff1f; 为什么要使用激活函数&#xff1f; 详细讲解激活函数 本专栏适合于Python已经入门的学生或人士&#xff0c;有一定的编程基础。本专栏适合于算法工程师、机器学习、图像处理求职的学生或人…...

chatglm3-6b部署及微调

chatglm3-6b部署及微调 modelscope: https://modelscope.cn/models/ZhipuAI/chatglm3-6b/filesgithub: https://github.com/THUDM/ChatGLM3镜像: ubuntu20.04-cuda11.7.1-py38-torch2.0.1-tf1.15.5-1.8.1v100 16G现存 单卡 安装 软件依赖 # 非必要无需执行 # pip install -…...

Hive 知识点八股文记录 ——(二)优化

函数 UDF&#xff1a;用户定义函数 UDAF&#xff1a;用户定义聚集函数 UDTF&#xff1a;用户定义表生成函数 建表优化 分区建桶 创建表时指定分区字段 PARTITIONED BY (date string)指定分桶字段和数量 CLUSTERED BY (id) INTO 10 BUCKETS插入数据按分区、分桶字段插入 …...

计算机技术专业CSIT883系统分析与项目管理介绍

文章目录 前言一、学科学习成果二、使用步骤三、最低出勤要求四、讲座时间表五、项目管理 前言 本课程介绍了信息系统开发中的技术和技术&#xff0c;以及与管理信息技术项目的任务相关的方法和过程。 它研究了系统分析师、客户和用户在系统开发生命周期中的互补角色。 它涵盖…...

gitlab安装地址

镜像地址&#xff1a; Index of /gitlab-ce/yum/el7/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror wget Index of /gitlab-ce/yum/el7/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror rpm -i gitlab-ce-15.9.1-ce.0.el7.x86_64.rpm 一直提示 &#x…...

Spark处理方法_提取文件名中的时间

需求描述 通过读取目录下的类似文件的datapath路径的文件名及文件内容&#xff0c;需要将读取的每一个文件的文件名日期解析出来&#xff0c;并作为读取当前文件内容递归读取当前文件一个df列&#xff0c;列名为“时间”&#xff1b;后面就是读一个文件&#xff0c;解析一下时间…...

技术分享 | 测试平台开发-前端开发之数据展示与分析

测试平台的数据展示与分析&#xff0c;我们主要使用开源工具ECharts来进行数据的展示与分析。 ECharts简介与安装 ECharts是一款基于JavaScript的数据可视化图表库&#xff0c;提供直观&#xff0c;生动&#xff0c;可交互&#xff0c;可个性化定制的数据可视化图表&#xff…...

NZ系列工具NZ06:VBA创建PDF文件说明

我的教程一共九套及VBA汉英手册一部&#xff0c;分为初级、中级、高级三大部分。是对VBA的系统讲解&#xff0c;从简单的入门&#xff0c;到数据库&#xff0c;到字典&#xff0c;到高级的网抓及类的应用。大家在学习的过程中可能会存在困惑&#xff0c;这么多知识点该如何组织…...

redis-cli 连接 sentinel架构的redis服务

之前一直用gui连接redis&#xff0c;今天在服务器连接发现redis-cli无法直接连接到redis-sentinel服务器&#xff0c;研究后发现多了几个步骤&#xff0c;如下&#xff1a; 假设有三个redis节点127.0.0.1,127.0.0.2,127.0.0.3,端口为9696先连接任意一个节点: redis-cli -h 12…...

使用github copilot

现在的大模型的应用太广了&#xff0c;作为程序员我们当然野可以借助大模型来帮我们敲代码。 下面是自己注册使用github copilot的过程。 一、注册github copilot 1. 需要拥有github账号 &#xff0c;登录github之后&#xff0c;点右侧自己的头像位置&#xff0c;下面会出现…...

1438 绝对差不超过限制的最长连续子数组(单调队列)

题目 绝对差不超过限制的最长连续子数组 给你一个整数数组 nums &#xff0c;和一个表示限制的整数 limit&#xff0c;请你返回最长连续子数组的长度&#xff0c;该子数组中的任意两个元素之间的绝对差必须小于或者等于 limit 。 如果不存在满足条件的子数组&#xff0c;则返…...

OpenCV入门9:图像增强和图像滤波

图像增强是一种通过对图像进行处理以改善其质量、对比度、清晰度等方面的技术。在OpenCV中&#xff0c;有多种图像增强的方法和函数可用。下面简要介绍一些常见的图像增强方法及其在OpenCV中的实现方式。 直方图均衡化&#xff08;Histogram Equalization&#xff09;&#xff…...

Pycharm常用快捷键和替换正则表达式

原生快捷键的使用&#xff1a; 1.CtrlF&#xff1a;查找 2.CtrlZ&#xff1a;返回上一步 3.Alt 鼠标左键选择&#xff1a;多行同时编辑&#xff08;上、下、左、右键能够移动光标&#xff09; 按住Ctrl,左键点击&#xff0c;定位光标 编辑过程 URL常用的替换正则表达式&am…...

C#,数值计算——函数计算,Epsalg的计算方法与源程序

1 文本格式 using System; namespace Legalsoft.Truffer { /// <summary> /// Convergence acceleration of a sequence by the algorithm.Initialize by /// calling the constructor with arguments nmax, an upper bound on the /// number of term…...

网盘直链下载助手终极指南:告别限速,一键获取8大网盘真实下载地址

网盘直链下载助手终极指南&#xff1a;告别限速&#xff0c;一键获取8大网盘真实下载地址 【免费下载链接】Online-disk-direct-link-download-assistant 一个基于 JavaScript 的网盘文件下载地址获取工具。基于【网盘直链下载助手】修改 &#xff0c;支持 百度网盘 / 阿里云盘…...

Charles + Proxifier 抓包实战:从环境搭建到疑难解析

1. 环境准备&#xff1a;搭建抓包基础设施 搞开发的朋友们应该都遇到过这样的场景&#xff1a;某个本地应用死活不走系统代理&#xff0c;你想抓它的包就像追一只不按套路跑的野猫。这时候CharlesProxifier的组合就像专业驯猫师&#xff0c;今天我就带你们从零开始搭建这个黄金…...

HY-Motion 1.0在VR开发中的应用:手势交互与动作捕捉替代方案

HY-Motion 1.0在VR开发中的应用&#xff1a;手势交互与动作捕捉替代方案 1. 引言 想象一下&#xff0c;你正在开发一款VR游戏&#xff0c;需要让虚拟角色做出"挥手打招呼"的动作。传统方式可能需要昂贵的动作捕捉设备&#xff0c;专业的动捕演员&#xff0c;以及数…...

AGI不是工具,是新物种:SITS2026圆桌首次公开127页《人机共生宪章》草案,含教育/就业/伦理三大断层应对路线图

第一章&#xff1a;AGI不是工具&#xff0c;是新物种&#xff1a;SITS2026圆桌的历史性宣言 2026奇点智能技术大会(https://ml-summit.org) 在SITS2026主会场“意识边界”圆桌现场&#xff0c;七位来自神经科学、认知哲学、自主系统伦理与AGI架构实验室的代表共同签署《非工具…...

终极指南:ET框架资源加载失败恢复机制——重试策略与用户引导全解析

终极指南&#xff1a;ET框架资源加载失败恢复机制——重试策略与用户引导全解析 【免费下载链接】ET Unity3D Client And C# Server Framework 项目地址: https://gitcode.com/GitHub_Trending/et/ET ET框架作为Unity3D客户端与C#服务器框架的佼佼者&#xff0c;其资源加…...

Spliit费用分摊算法解析:从等额分摊到按比例分配的完整实现

Spliit费用分摊算法解析&#xff1a;从等额分摊到按比例分配的完整实现 【免费下载链接】spliit Free and Open Source Alternative to Splitwise. Share expenses with your friends and family. 项目地址: https://gitcode.com/gh_mirrors/sp/spliit Spliit作为一款免…...

启动瓶颈定位实战:Perfetto + Macrobenchmark 一套组合拳

上一篇我们画了一张完整的冷启动全景图&#xff0c;从 Launcher 点击到 Fully Drawn 的七个阶段都拆开看了一遍。理解全景图是前提&#xff0c;但只有全景图是不够的——你知道时间花在了"某个阶段"&#xff0c;但具体是哪行代码、哪个初始化拖慢了整个链路&#xff…...

如何通过 reflect.Value 获取切片的底层值

go 的 reflect.value 没有提供通用的 slice() 方法&#xff0c;因为无法定义一个适用于所有切片类型的返回签名&#xff1b;正确方式是调用 interface() 后配合类型断言获取原始切片。 go 的 reflect.value 没有提供通用的 slice() 方法&#xff0c;因为无法定义一个适用于…...

从何凯明的MAE项目看timm:如何像大佬一样复用模块构建自定义ViT

从何凯明的MAE项目看timm&#xff1a;如何像大佬一样复用模块构建自定义ViT 在计算机视觉领域&#xff0c;timm库&#xff08;PyTorch Image Models&#xff09;已经成为研究人员和工程师不可或缺的工具箱。这个由Ross Wightman维护的开源项目不仅提供了数百个预训练模型&#…...

从‘solver not found’到成功求解:YALMIP调用CPLEX的完整排错手册

从‘solver not found’到成功求解&#xff1a;YALMIP调用CPLEX的完整排错手册 当你在MATLAB中安装好YALMIP和CPLEX&#xff0c;满怀期待地运行yalmiptest看到CPLEX显示为"found"&#xff0c;却在真正求解自己的优化模型时遭遇各种报错——这种从希望到挫败的落差感&…...