6.11行为型---解释器模式

2023-10-27

在软件开发中,会遇到有些问题多次重复出现,而且有一定的相似性和规律性。如果将它们归纳成一种简单的语言,那么这些问题实例将是该语言的一些句子,这样就可以用“编译原理”中的解释器模式来实现了。

虽然使用解释器模式的实例不是很多,但对于满足以上特点,且对运行效率要求不是很高的应用实例,如果用解释器模式来实现,其效果是非常好的,本文将介绍其工作原理与使用方法。

模式的定义与特点

解释器(Interpreter)模式的定义:给分析对象定义一个语言,并定义该语言的文法表示,再设计一个解析器来解释语言中的句子。也就是说,用编译语言的方式来分析应用中的实例。这种模式实现了文法表达式处理的接口,该接口解释一个特定的上下文。

这里提到的文法和句子的概念同编译原理中的描述相同,“文法”指语言的语法规则,而“句子”是语言集中的元素。例如,汉语中的句子有很多,“我是中国人”是其中的一个句子,可以用一棵语法树来直观地描述语言中的句子。

解释器模式是一种类行为型模式,其主要优点如下。

  1. 扩展性好。由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法。
  2. 容易实现。在语法树中的每个表达式节点类都是相似的,所以实现其文法较为容易。


解释器模式的主要缺点如下。

  1. 执行效率较低。解释器模式中通常使用大量的循环和递归调用,当要解释的句子较复杂时,其运行速度很慢,且代码的调试过程也比较麻烦。
  2. 会引起类膨胀。解释器模式中的每条规则至少需要定义一个类,当包含的文法规则很多时,类的个数将急剧增加,导致系统难以管理与维护。
  3. 可应用的场景比较少。在软件开发中,需要定义语言文法的应用实例非常少,所以这种模式很少被使用到。

模式的结构与实现

解释器模式常用于对简单语言的编译或分析实例中,为了掌握好它的结构与实现,必须先了解编译原理中的“文法、句子、语法树”等相关概念。

1) 文法

文法是用于描述语言的语法结构的形式规则。没有规矩不成方圆,例如,有些人认为完美爱情的准则是“相互吸引、感情专一、任何一方都没有恋爱经历”,虽然最后一条准则较苛刻,但任何事情都要有规则,语言也一样,不管它是机器语言还是自然语言,都有它自己的文法规则。例如,中文中的“句子”的文法如下。

〈句子〉::=〈主语〉〈谓语〉〈宾语〉
〈主语〉::=〈代词〉|〈名词〉
〈谓语〉::=〈动词〉
〈宾语〉::=〈代词〉|〈名词〉
〈代词〉你|我|他
〈名词〉7大学生I筱霞I英语
〈动词〉::=是|学习


注:这里的符号“::=”表示“定义为”的意思,用“〈”和“〉”括住的是非终结符,没有括住的是终结符。

2) 句子

句子是语言的基本单位,是语言集中的一个元素,它由终结符构成,能由“文法”推导出。例如,上述文法可以推出“我是大学生”,所以它是句子。

3) 语法树

语法树是句子结构的一种树型表示,它代表了句子的推导结果,它有利于理解句子语法结构的层次。图 1 所示是“我是大学生”的语法树。
 

句子“我是大学生”的语法树
图1 句子“我是大学生”的语法树


有了以上基础知识,现在来介绍解释器模式的结构就简单了。解释器模式的结构与组合模式相似,不过其包含的组成元素比组合模式多,而且组合模式是对象结构型模式,而解释器模式是类行为型模式。

1. 模式的结构

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

  1. 抽象表达式(Abstract Expression)角色:定义解释器的接口,约定解释器的解释操作,主要包含解释方法 interpret()。
  2. 终结符表达式(Terminal    Expression)角色:是抽象表达式的子类,用来实现文法中与终结符相关的操作,文法中的每一个终结符都有一个具体终结表达式与之相对应。
  3. 非终结符表达式(Nonterminal Expression)角色:也是抽象表达式的子类,用来实现文法中与非终结符相关的操作,文法中的每条规则都对应于一个非终结符表达式。
  4. 环境(Context)角色:通常包含各个解释器需要的数据或是公共的功能,一般用来传递被所有解释器共享的数据,后面的解释器可以从这里获取这些值。
  5. 客户端(Client):主要任务是将需要分析的句子或表达式转换成使用解释器对象描述的抽象语法树,然后调用解释器的解释方法,当然也可以通过环境角色间接访问解释器的解释方法。


解释器模式的结构图如图 2 所示。
 

解释器模式的结构图
图2 解释器模式的结构图

2. 模式的实现

解释器模式实现的关键是定义文法规则、设计终结符类与非终结符类、画出结构图,必要时构建语法树,其代码结构如下:

 
//抽象表达式类
interface AbstractExpression
{
public Object interpret(String info); //解释方法
}
//终结符表达式类
class TerminalExpression implements AbstractExpression
{
public Object interpret(String info)
{
//对终结符表达式的处理
}
}
//非终结符表达式类
class NonterminalExpression implements AbstractExpression
{
private AbstractExpression exp1;
private AbstractExpression exp2;
public Object interpret(String info)
{
//非对终结符表达式的处理
}
}
//环境类
class Context
{
private AbstractExpression exp;
public Context()
{
//数据初始化
}
public void operation(String info)
{
//调用相关表达式类的解释方法
}
}

 

模式的应用实例

【例1】用解释器模式设计一个“韶粵通”公交车卡的读卡器程序。

说明:假如“韶粵通”公交车读卡器可以判断乘客的身份,如果是“韶关”或者“广州”的“老人” “妇女”“儿童”就可以免费乘车,其他人员乘车一次扣 2 元。

分析:本实例用“解释器模式”设计比较适合,首先设计其文法规则如下。

<expression> ::= <city>的<person>
<city> ::= 韶关|广州
<person> ::= 老人|妇女|儿童


然后,根据文法规则按以下步骤设计公交车卡的读卡器程序的类图。

  • 定义一个抽象表达式(Expression)接口,它包含了解释方法 interpret(String    info)。
  • 定义一个终结符表达式(Terminal Expression)类,它用集合(Set)类来保存满足条件的城市或人,并实现抽象表达式接口中的解释方法 interpret(Stringinfo),用来判断被分析的字符串是否是集合中的终结符。
  • 定义一个非终结符表达式(AndExpressicm)类,它也是抽象表达式的子类,它包含满足条件的城市的终结符表达式对象和满足条件的人员的终结符表达式对象,并实现 interpret(String info) 方法,用来判断被分析的字符串是否是满足条件的城市中的满足条件的人员。
  • 最后,定义一个环境(Context)类,它包含解释器需要的数据,完成对终结符表达式的初始化,并定义一个方法 freeRide(String info) 调用表达式对象的解释方法来对被分析的字符串进行解释。其结构图如图 3 所示。

 

“韶粵通”公交车读卡器程序的结构图
图3 “韶粵通”公交车读卡器程序的结构图


程序代码如下:

 
package interpreterPattern;
import java.util.*;
/*文法规则
<expression> ::= <city>的<person>
<city> ::= 韶关|广州
<person> ::= 老人|妇女|儿童
*/
public class InterpreterPatternDemo
{
public static void main(String[] args)
{
Context bus=new Context();
bus.freeRide("韶关的老人");
bus.freeRide("韶关的年轻人");
bus.freeRide("广州的妇女");
bus.freeRide("广州的儿童");
bus.freeRide("山东的儿童");
}
}
//抽象表达式类
interface Expression
{
public boolean interpret(String info);
}
//终结符表达式类
class TerminalExpression implements Expression
{
private Set<String> set= new HashSet<String>();
public TerminalExpression(String[] data)
{
for(int i=0;i<data.length;i++)set.add(data[i]);
}
public boolean interpret(String info)
{
if(set.contains(info))
{
return true;
}
return false;
}
}
//非终结符表达式类
class AndExpression implements Expression
{
private Expression city=null;
private Expression person=null;
public AndExpression(Expression city,Expression person)
{
this.city=city;
this.person=person;
}
public boolean interpret(String info)
{
String s[]=info.split("的");
return city.interpret(s[0])&&person.interpret(s[1]);
}
}
//环境类
class Context
{
private String[] citys={"韶关","广州"};
private String[] persons={"老人","妇女","儿童"};
private Expression cityPerson;
public Context()
{
Expression city=new TerminalExpression(citys);
Expression person=new TerminalExpression(persons);
cityPerson=new AndExpression(city,person);
}
public void freeRide(String info)
{
boolean ok=cityPerson.interpret(info);
if(ok) System.out.println("您是"+info+",您本次乘车免费!");
else System.out.println(info+",您不是免费人员,本次乘车扣费2元!");
}
}

 


程序运行结果如下:

您是韶关的老人,您本次乘车免费!
韶关的年轻人,您不是免费人员,本次乘车扣费2元!
您是广州的妇女,您本次乘车免费!
您是广州的儿童,您本次乘车免费!
山东的儿童,您不是免费人员,本次乘车扣费2元!

模式的应用场景

前面介绍了解释器模式的结构与特点,下面分析它的应用场景。

  1. 当语言的文法较为简单,且执行效率不是关键问题时。
  2. 当问题重复出现,且可以用一种简单的语言来进行表达时。
  3. 当一个语言需要解释执行,并且语言中的句子可以表示为一个抽象语法树的时候,如 XML 文档解释。


注意:解释器模式在实际的软件开发中使用比较少,因为它会引起效率、性能以及维护等问题。如果碰到对表达式的解释,在 Java 中可以用 Expression4J 或 Jep 等来设计。

模式的扩展

在项目开发中,如果要对数据表达式进行分析与计算,无须再用解释器模式进行设计了,Java 提供了以下强大的数学公式解析器:Expression4J、MESP(Math Expression String Parser) 和 Jep 等,它们可以解释一些复杂的文法,功能强大,使用简单。

现在以 Jep 为例来介绍该工具包的使用方法。Jep 是 Java expression parser 的简称,即 Java 表达式分析器,它是一个用来转换和计算数学表达式的 Java 库。通过这个程序库,用户可以以字符串的形式输入一个任意的公式,然后快速地计算出其结果。而且 Jep 支持用户自定义变量、常量和函数,它包括许多常用的数学函数和常量。

使用前先下载 Jep 压缩包,解压后,将 jep-x.x.x.jar 文件移到选择的目录中,在 Eclipse 的“Java 构建路径”对话框的“库”选项卡中选择“添加外部 JAR(X)...”,将该 Jep 包添加项目中后即可使用其中的类库。

下面以计算存款利息为例来介绍。存款利息的计算公式是:本金x利率x时间=利息,其相关代码如下:

 
  1. package interpreterPattern;
    import com.singularsys.jep.*;
    public class JepDemo
    {
    public static void main(String[] args) throws JepException
    {
    Jep jep=new Jep();
    //定义要计算的数据表达式
    String 存款利息="本金*利率*时间";
    //给相关变量赋值
    jep.addVariable("本金",10000);
    jep.addVariable("利率",0.038);
    jep.addVariable("时间",2);
    jep.parse(存款利息); //解析表达式
    Object accrual=jep.evaluate(); //计算
    System.out.println("存款利息:"+accrual);
    }
    }

     


程序运行结果如下:

存款利息:760.0
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

6.11行为型---解释器模式 的相关文章

  • 计算机网络毕业论文ensp,eNSP实验(一)简单组网实验

    一 前言 计算机网络在很久之前学习过 最近突然对网络安全领域比较感兴趣 所以通过实验的形式重新温习一下计算机网络的相关知识 eNSP实验系列借助华为eNSP软件完成相关实验的组网设计 实验所需的工具包括eNSP软件 Wireshark抓包工
  • React 学习之旅一

    React 学习之旅一 React 官网 版本要求 Node gt 8 10 和 npm gt 5 6 node v npm v 创建 React 项目 npx create react app my app Hook 它可以让你在不编写

随机推荐

  • customEventData的普通用法

    需要先用event来接受一些其他信息 然后传入的数值放在参数中 类型按钮按下 TypeButtonDown event value console log 按下 console log value 然后再button这里设置好eventDa
  • java 实现POI导入导出Excel

    package com chenxc utils excel import org apache poi hssf usermodel import org apache poi hssf util HSSFColor import org
  • 基于Django Template模式实现简单的注册、登录,表单提交

    最近在做一个简单的django项目 需求是能实现注册 登录 并且有一个提交评论 展示评论的功能 django做web项目是非常适合 前期熟悉对应的配置 熟悉框架结构 按照既定的模式进行二次开发即可完成一个完整的系统 项目目录结构如下 1 核
  • GPT4的1000+篇文章总结

    GPT4的1000 篇文章总结 本文收集和总结了有关GPT4的1000 篇文章 由于篇幅有限只能总结近期的内容 想了解更多内容可以访问 http www ai2news com 其分享了有关AI的论文 文章 图书 query 恐怖的GPT4
  • 页面触底自动加载 Vue 组件

    不管是 web 端还是移动端 信息流都是现在很流行的信息展示方式 信息流经常搭配自动加载一起使用以获得更好的使用体验 最近在使用 Vue 开发过程中也遇到了首页信息流自动加载的需求 大致了解了一下几个滚动自动加载组件 发现多数都是把内容放在
  • 如何理解jquery的$(".span").eq(0) 和 $(”.span“)[0]

    span class span 1 span span class span 2 span span class span 3 span span class span 4 span span 选择了4个span 都是jquery对象 可以
  • 第一章:走进scratch3.0

    第一章 Scratch中国 想象 创作 编程 分享 scratch cn cn 离线版桌面版下载 exe结尾是 windows版 dmg是 macOS 苹果 版 点击下方链接下载 链接 https pan baidu com s 1wwrK
  • ALtium 16等长布线、差分布线

    等长布线 1 先布好线 2 定义一个class design classes 下图右键net classes add classes 之后定义这个class的名字 例如dc 选择要等长的网络加入这个dc 之后close即可 3 tools
  • 毕业设计-基于机器学习的股票预测

    目录 前言 课题背景和意义 实现技术思路 一 传统股票预测模型 二 新型股票预测方法 实现效果图样例 最后 前言 大四是整个大学期间最忙碌的时光 一边要忙着备考或实习为毕业后面临的就业升学做准备 一边要为毕业设计耗费大量精力 近几年各个学校
  • ImportError: No module named psutil的解决

    系统版本 cat etc redhat release CentOS release 6 9 Final 跑python脚本的时候报错ImportError No module named psutil 于是想要用pip来安装 但是发现pi
  • 关于source中的TARGETLIBS和SOURCELIBS的区别

    摘自 http topic csdn net u 20090403 11 897684cc 3eca 40f4 90d7 88e501669efe html 转自微软MVP作者 ARM WinCE 1 Dirs文件 关于Dirs文件 就是指
  • Win10 环境配置 Github SSH Key

    使用 Git 关联远程仓库通常可以使用 HTTP 协议或者 SSH 协议 在使用上 SSH 只需要一次配置 之后提交操作都不需要进行用户密码验证 但是 HTTP 方式每次 Push 操作都需要验证用户名和密码 当然 HTTP 方式更方便开源
  • 互联网摸鱼日报(2023-09-13)

    互联网摸鱼日报 2023 09 13 36氪新闻 节能减排 科技降碳 上海打造绿色商场的N种实践 36氪独家 美团管理层迎来重大变化 5位业务负责人晋升为副总裁 险资牵手上市公司狂出手 800亿给了谁 没空旅行的年轻人 开始互换特产 国产手
  • 手把手教你用python一键抢12306火车票(附代码)

    哈喽 哈喽 一年一度的抢火车票大战正式拉开序幕 然饿大多数人碰到的是这种情况 当你满心期待摩拳擦掌准备抢票的时候 你会发现一票难求 想回趟家真难 那么作为程序猿的你 当然要用程序猿的方式来抢票 下面分享用python来抢票 欢迎关注公众号
  • 电脑照片,怎么把电脑照片传到iphone手机 将电脑照片传到iphone方法【图文】

    使用苹果设备的人一定知道苹果自带的iOS系统是不可以随便进行数据的交换的 必须使用iTunes软件进行传输 但是让一些对不懂苹果设备的人带来了很多困扰 电脑当中的图片如何传输到苹果设备当中呢 下面我们就一起来看看怎么把电脑照片传到iphon
  • ZOJ 1610 Count the Colors

    Problem acm zju edu cn onlinejudge showProblem do problemCode 1610 Reference blog csdn net shuangde800 article details 8
  • 单元二:全桥MOS/IGBT电路(后端全桥电路的搭建)

    本篇博客是全桥MOS IGBT电路搭建的介绍 想了解全桥电路的驱动部分请看博主的单元一 全桥驱动电路详解 感兴趣的可以添加博主QQ 2859340499 B站对应讲解本文链接 逆变电路 Inverter Circuit 是与整流电路 Rec
  • 华为荣耀七能升级鸿蒙系统吗,华为鸿蒙系统来了,你知道哪些华为手机荣耀手机可以升级吗?...

    从鸿蒙系统第一次开始登场 到现在慢慢有许多鸿蒙系统设备出现 手机市场的格局似乎又要升级变化了 科技树儿了解到 在某数码博主经过和相关人员的沟通核实之后 目前暂定的是搭载华为麒麟710芯片以上的机型 无论华为或荣耀 都会升级华为鸿蒙Harmo
  • 深入理解Java比较器(Comparable和Comparator)

    深入理解Java比较器 Comparable和Comparator 文章目录 深入理解Java比较器 Comparable和Comparator 一 Comparable 1 Comparable 接口定义 二 Comparator 比较器
  • 6.11行为型---解释器模式

    在软件开发中 会遇到有些问题多次重复出现 而且有一定的相似性和规律性 如果将它们归纳成一种简单的语言 那么这些问题实例将是该语言的一些句子 这样就可以用 编译原理 中的解释器模式来实现了 虽然使用解释器模式的实例不是很多 但对于满足以上特点