Java设计模式之行为型-解释器模式(UML类图+案例分析)

目录

一、基础概念

二、UML类图

三、角色设计

四、案例分析

五、总结


 

一、基础概念

解释器模式是指给定一个语言(表达式),来表示它的文法,并定义一个解释器,使用该解释器来解释语言中的句子(表达式),并得到结果。简单来说,解释器模式把要处理的问题条条框框地定义出来,然后挨个细致解释,适合一些重复性的问题求解。

所谓终结符,是指表达式中的最小单位,它不再包含其他更小的表达式,可以理解为表达式的“叶子”节点,例如在一个简单的表达式计算器中:

1、数字值(如123)就是一个终结符,数字解释器实现了对数字的解释操作。

2、变量名(如a)也可以看作是一个终结符,变量解释器实现获取变量的值。

非终结符需要通过组合包含的子表达式的结果进行解释,例如:

1、加法表达式包含左边和右边两个子表达式,通过组合两个子表达式的计算结果实现加法。

2、函数调用表达式包含函数名和参数表达式,需要调用函数并传入参数计算结果。

举个例子:"5 + 6",然后解析这个表达式,计算出结果11。

在这个例子中:

1、数字5和6是终结符,它们是表达式的基本元素,不再包含其他表达式。

2、加号是非终结符,它表示一个表达式,需要通过计算5和6才能得出值。

所以简单来说:

1、终结符是表达式的基本元素,不再含其他表达式。

2、非终结符表示一个表达式,包含其他表达式。

二、UML类图

三、角色设计

角色描述
抽象表达式角色定义解释器的接口,声明一个解释操作interpret()
终结符表达式角色实现抽象表达式接口,表示语法规则中的终结符
非终结符表达式角色实现抽象表达式接口,表示语法规则中的非终结符,可以包含终结符和非终结符
客户端角色建立解释器对象,并调用interpret()方法进行解释操作

四、案例分析

下面我们通过模拟一个算术表达式的运算过程来实现解释器模式。

大体的工作流程如下:

1、构建表达式语法树。

2、用递归解释器对语法树进行解释执行。

3、终结符表达式直接返回值,非终结符表达式通过组合运算达到计算功能。

4、客户端构建语法树并调用解释器执行,即完成了对表达式的解析和求值。

代码设计如下:

定义一个抽象表达式角色,并定义了解释操作接口interpret():

public interface Expression {int interpret();}

定义AddExpression,SubtractExpression等,非终结符表达式角色,它们实现了对复合表达式的解释,通过组合包含的子表达式的解释结果进行解释:

public class AddExpression implements Expression {private Expression left;private Expression right;public AddExpression(Expression left, Expression right) {this.left = left;this.right = right;}@Overridepublic int interpret() {return left.interpret() + right.interpret();}
}public class SubtractExpression implements Expression {private Expression left;private Expression right;public SubtractExpression(Expression left, Expression right) {this.left = left;this.right = right;}@Overridepublic int interpret() {return left.interpret() - right.interpret();}
}public class MultiplyExpression implements Expression {private Expression left;private Expression right;public MultiplyExpression(Expression left, Expression right) {this.left = left;this.right = right;}@Overridepublic int interpret() {return left.interpret() * right.interpret();}
}public class DivideExpression implements Expression {private Expression left;private Expression right;public DivideExpression(Expression left, Expression right) {this.left = left;this.right = right;}@Overridepublic int interpret() {try {return left.interpret() / right.interpret();} catch (ArithmeticException e) {System.out.println("除数不能为0");return -1;}}
}

定义NumberExpression,终结符表达式角色,它实现了对终结符的解释,这里的终结符是数字,直接返回数字的值:

public class NumberExpression implements Expression {private int number;public NumberExpression(int number) {this.number = number;}@Overridepublic int interpret() {return number;}}

客户端角色,添加一个Interpreter类,来解析表达式字符串,构建抽象语法树,并调用interpret()方法:

public class Client {public static void main(String[] args) {Expression expression = new SubtractExpression(new AddExpression(new NumberExpression(5),new MultiplyExpression(new NumberExpression(10), new NumberExpression(15))),new DivideExpression(new NumberExpression(5), new NumberExpression(5)));int result = expression.interpret();System.out.println("计算结果为:" + result);}
}

运行结果如下:

执行流程如下:

1、在客户端中,首先构建一个减法表达式SubtractExpression,它的左表达式是加法表达式AddExpression,右表达式是除法表达式DivideExpression

2、加法表达式AddExpression的左表达式是数字表达式NumberExpression(5),右表达式是乘法表达式MultiplyExpression

3、乘法表达式MultiplyExpression的左表达式是数字表达式NumberExpression(10),右表达式是数字表达式NumberExpression(15)

4、除法表达式DivideExpression的左表达式是数字表达式NumberExpression(5),右表达式是数字表达式NumberExpression(5)

5、这样就构建出了一个完整的表达式树。

6、然后调用这个减法表达式的interpret()方法进行解释执行。

7interpret()方法会递归调用子表达式的interpret()方法,终结于数字表达式的interpret()只返回数字的值。

8、这样整个表达式树就得到了解释执行,得到最终结果。

9、这里的执行顺序是:数字表达式->乘法表达式->加法表达式->除法表达式->减法表达式,逐步对表达式树进行解释,得到最终的值。

所以这个案例利用了解释器模式,定义了表达式接口,并为不同的表达式实现了interpret()方法,通过组合模式构建表达式树,然后递归解释执行以求值。 

五、总结

优点:

1、可扩展性好。新增解释表达式较为方便,符合开闭原则。

2、易于改变和扩展语法。比如增加新的运算符等。

3、将语法解释与执行分离,允许程序动态地定制,每个节点只需要处理自身逻辑。

4、各部分功能模块较为独立,有利于实现和维护。

5、使用递归解释方式简化了语法树的evaluate逻辑。

缺点

1、解释过程效率较低,递归调用开销较大。

2、类数量多,之间依赖关系复杂,导致系统较难理解。

3、语法树构建较为麻烦,需要客户端理解语法结构。

4、语法树的解析需要定义大量的类,不易管理。

5、语言扩展较难,需要更改多个表达式类。

6、错误处理不容易,需要增加额外处理。

7、调试过程较复杂。

应用场景:

1、语言解释器:用于解析和解释语言中的表达式,如编译器、解释器等。这个代码案例就展示了如何通过解释器模式解析算术表达式。

2、表达式求值:用于进行表达式求值,如正则表达式、数学表达式、逻辑表达式等的求值运算。

3、符号处理引擎:可用来进行数学符号、表达式的解析。

4、语法分析:可构建语法树,用来分析句子或表达式所遵循的语法规则。

5、业务规则解析:可定义业务规则语法,并进行解释执行,用来实现规则引擎。

6、格式转换器:可将一种格式转换为另一种格式。

符合的设计原则:

1、开闭原则(Open Closed Principle)

解释器模式中的解释器语法可以通过继承扩展,而无需修改原有解释器代码,符合开闭原则。

2、单一职责原则(Single Responsibility Principle)

解释器模式把语法解析的任务分发给不同的解释器类,每一个解释器只负责一小部分语法规则,符合单一职责原则。

3、组合复用原则(Composite Reuse Principle)

解释器模式中的解释器可以通过组合方式重用,符合组合复用原则。

4、接口隔离原则(Interface Segregation Principle)

解释器模式通过定义解释器接口将解析语法和解释执行分离,符合接口隔离原则。

5、依赖倒转原则(Dependence Inversion Principle)

解释器类都依赖一个抽象语法树而不是具体语法类,符合依赖倒转原则。

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

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

相关文章

【文末送书 - 数据分析之pandas篇④】- DataFrame数据合并

向阳花花花花 - 个人主页 迄今所有人生都大写着失败,但并不妨碍我继续向前 Python 数据分析专栏 正在火热更新中 🔥 文章目录 一、concat二、append三、merge3.1 没有属性相同时3.2 只有一个属性相同时1.一对一合并2.一对多合并3.多对多合并 3.3 有多个…

Ceph 应用

Ceph 应用 一、创建 CephFS 文件系统 MDS 接口 1.服务端操作 1)在管理节点创建 mds 服务 cd /etc/ceph ceph-deploy mds create node01 node02 node032)查看各个节点的 mds 服务 ssh rootnode01 systemctl status ceph-mdsnode01 ssh rootnode02 syst…

ELK 企业级日志分析系统(三)

ELK 一、Zookeeper理论部分zookeeper的定义与工作机制zookeeper的特点Zookeeper 数据结构Zookeeper 应用场景Zookeeper 选举机制 二、zookeeper部署实验三、Kafka消息队列为什么需要消息队列(MQ)使用消息队列的好处消息队列的两种模式 Kafka 定义Kafka 简…

笔试题之地区经济数据分析

数据分析通常应用于商业领域,但对于政府、非盈利组织等机构而言,在考量城市发展、监控环境质量等方面,也会涉及到数据分析。这时,就需要我们根据实际场景,结合数据分析的理论知识,发现其中的规律&#xff0…

人工智能发展前夜,基于控制论的杂谈

谢邀。 目录 《What the Frogs Eye Tells the Frogs Brain?》简介我是怎么理解这篇文章的?被后世频繁引用的「青蛙」从「青蛙」再重新转向控制论 《What the Frog’s Eye Tells the Frog’s Brain?》简介 帕斯克在实践和理论中,将人类的适应性行为引入…

怎样优雅地增删查改(八):按用户关系查询

文章目录 原理实现正向用户关系反向用户关系 使用测试 用户关系(Relation)是描述业务系统中人员与人员之间的关系,如:签约、关注,或者朋友关系。 之前我们在扩展身份管理模块的时候,已经实现了用户关系管理…

EC200A-CN移植

Platform: RK3588 OS: Debian11 Kernel: v5.10.160 Module :EC200A-CN 国内版 Linux USB 驱动架构 USB 主机控制器驱动在分层结构的最底层,直接与硬件交互。USB 核心是整个 USB 主机驱动的核心,用于管理 USB 总线、USB 总线设备和 USB 总线…

springboot整合eureka、config搭建注册中心和配置中心

目录 一 、springboot整合eureka实现注册中心 二、springboot整合config实现配置中心 三、从配置中心拉取配置 这篇文章详细介绍怎么通过eureka和config分别搭建一个注册中心和配置中心的服务。 一 、springboot整合eureka实现注册中心 1、创建一个springboot项目&#xff…

vue3 兄弟子组件相互调用方法的实现思路及解决方法

需求背景: vue实际开发过程中,可能需要在某一个子组件调用另一个子组件的方法,从而实现业务需求。 例如以下的一个业务场景。 如上就涉及到到组件B需要调用组件A的form验证方法。 解决思路: 利用共同的父组件C,我…

Spark 4:Spark Core 共享变量

广播变量 # coding:utf8 import timefrom pyspark import SparkConf, SparkContext from pyspark.storagelevel import StorageLevelif __name__ __main__:conf SparkConf().setAppName("test").setMaster("local[*]")sc SparkContext(confconf)stu_inf…

ES6——Promise

promise 含义:异步编程解决方案 特点:1、状态不受外界影响,状态有三种:pending、fulfilled、rejected 2、状态不可逆,只能pending -> fulfilled、pending -> rejected 缺点:无法取消、不设置回调函…

mysql数字开头字符串排序

表结构 CREATE TABLE building (id bigint NOT NULL,name varchar(255) CHARACTER SET utf8mb3 COLLATE utf8_general_ci DEFAULT NULL COMMENT 名称,full_name varchar(255) CHARACTER SET utf8mb3 COLLATE utf8_general_ci DEFAULT NULL COMMENT 全称,PRIMARY KEY (id) USIN…