设计模式
设计模式简介
看懂UML类图和时序图
UML统一建模语言
UML类图及类图之间的关系
类关系记忆技巧
如何正确使用设计模式
优秀设计的特征
面向对象设计原则
创建型设计模式
工厂模式
抽象工厂模式
简单工厂模式
静态工厂模式(Static Factory)
单例模式
建造者模式
原型模式
结构型设计模式
适配器模式
桥接模式
组合模式
装饰器模式
外观模式
享元模式
代理模式
过滤器模式
注册模式(Registry)
行为型设计模式
责任链模式
命令模式
解释器模式
中介者模式
备忘录模式
迭代器模式
观察者模式
状态模式
策略模式
模板模式
访问者模式
规格模式(Specification)
J2EE 设计模式
MVC 模式
业务代表模式
组合实体模式
数据访问对象模式(DAO模式)
前端控制器模式
拦截过滤器模式
空对象模式
服务定位器模式
传输对象模式
数据映射模式(Data Mapper)
依赖注入模式(Dependency Injection)
流接口模式(Fluent Interface)
其他模式
对象池模式(Pool)
委托模式
资源库模式(Repository)
实体属性值模式(EAV 模式)
反面模式
归纳设计模式
本文档使用 MrDoc 发布
-
+
首页
解释器模式
> 解释器模式提供了评估语言的语法或表达式的方式,它属于行为型模式。 ![解释器模式](/media/202203/2022-03-11_1920140.7990041377820046.png) 解释器模式(Interpreter Pattern)提供了评估语言的语法或表达式的方式,它属于行为型模式。这种模式实现了一个表达式接口,该接口解释一个特定的上下文。这种模式被用在 SQL 解析、符号处理引擎等。 ## 问题 在软件开发中,会遇到有些问题多次重复出现,而且有一定的相似性和规律性。如果将它们归纳成一种简单的语言,那么这些问题实例将是该语言的一些句子,这样就可以用“编译原理”中的解释器模式来实现了。 ## 介绍 **定义:** 给分析对象定义一个语言,并定义该语言的文法表示,再设计一个解析器来解释语言中的句子。也就是说,用编译语言的方式来分析应用中的实例。这种模式实现了文法表达式处理的接口,该接口解释一个特定的上下文。 这里提到的文法和句子的概念同编译原理中的描述相同,“文法”指语言的语法规则,而“句子”是语言集中的元素。例如,汉语中的句子有很多,“我是中国人”是其中的一个句子,可以用一棵语法树来直观地描述语言中的句子。 **意图:** 给定一个语言,定义它的文法表示,并定义一个解释器,这个解释器使用该标识来解释语言中的句子。 **主要解决:** 对于一些固定文法构建一个解释句子的解释器。 **何时使用:** 如果一种特定类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子。这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题。 **如何解决:** 构建语法树,定义终结符与非终结符。 **关键代码:** 构建环境类,包含解释器之外的一些全局信息,一般是 HashMap。 **应用实例:** 编译器、运算表达式计算。 **优点:** - 可扩展性比较好,灵活。 - 增加了新的解释表达式的方式。 - 易于实现简单文法。 **缺点:** - 可利用场景比较少。 - 对于复杂的文法比较难维护。 - 解释器模式会引起类膨胀。 - 解释器模式采用递归调用方法。 **使用场景:** - 可以将一个需要解释执行的语言中的句子表示为一个抽象语法树。 - 一些重复出现的问题可以用一种简单的语言来进行表达。 - 一个简单语法需要解释的场景。 **注意事项:** 可利用场景比较少,JAVA 中如果碰到可以用 expression4J 代替。 ## 结构 ### 概念 解释器模式常用于对简单语言的编译或分析实例中,为了掌握好它的结构与实现,必须先了解编译原理中的“文法、句子、语法树”等相关概念。 #### 文法 **文法是用于描述语言的语法结构的形式规则**。没有规矩不成方圆,例如,有些人认为完美爱情的准则是“相互吸引、感情专一、任何一方都没有恋爱经历”,虽然最后一条准则较苛刻,但任何事情都要有规则,语言也一样,不管它是机器语言还是自然语言,都有它自己的文法规则。例如,中文中的“句子”的文法如下。 ``` 〈句子〉::=〈主语〉〈谓语〉〈宾语〉 〈主语〉::=〈代词〉|〈名词〉 〈谓语〉::=〈动词〉 〈宾语〉::=〈代词〉|〈名词〉 〈代词〉你 | 我 | 他 〈名词〉7 大学生 I 筱霞 I 英语 〈动词〉::=是 | 学习 ``` 注:这里的符号“::=”表示“定义为”的意思,用“〈”和“〉”括住的是非终结符,没有括住的是终结符。 #### 句子 **句子是语言的基本单位,是语言集中的一个元素,它由终结符构成,能由“文法”推导出**。例如,上述文法可以推出“我是大学生”,所以它是句子。 #### 语法树 **语法树是句子结构的一种树型表示**,它代表了句子的推导结果,它有利于理解句子语法结构的层次。图中所示是“我是大学生”的语法树。 ![句子“我是大学生”的语法树](/media/202203/2022-03-10_1403220.8760724565480413.png) 有了以上基础知识,现在来介绍解释器模式的结构就简单了。解释器模式的结构与组合模式相似,不过其包含的组成元素比组合模式多,而且组合模式是对象结构型模式,而解释器模式是类行为型模式。 ### 模式结构 ![解释器模式的结构图](/media/202203/2022-03-10_1406290.9823303627922952.png) 解释器模式包含以下主要角色。 1. 抽象表达式(Abstract Expression)角色:定义解释器的接口,约定解释器的解释操作,主要包含解释方法 interpret()。 2. 终结符表达式(Terminal Expression)角色:是抽象表达式的子类,用来实现文法中与终结符相关的操作,文法中的每一个终结符都有一个具体终结表达式与之相对应。 3. 非终结符表达式(Nonterminal Expression)角色:也是抽象表达式的子类,用来实现文法中与非终结符相关的操作,文法中的每条规则都对应于一个非终结符表达式。 4. 环境(Context)角色:通常包含各个解释器需要的数据或是公共的功能,一般用来传递被所有解释器共享的数据,后面的解释器可以从这里获取这些值。 5. 客户端(Client):主要任务是将需要分析的句子或表达式转换成使用解释器对象描述的抽象语法树,然后调用解释器的解释方法,当然也可以通过环境角色间接访问解释器的解释方法。 ### 实例 我们将创建一个接口 *Expression* 和实现了 *Expression* 接口的实体类。定义作为上下文中主要解释器的 *TerminalExpression* 类。其他的类 *OrExpression* 、*AndExpression* 用于创建组合式表达式。 *InterpreterPatternDemo* ,我们的演示类使用 *Expression* 类创建规则和演示表达式的解析。 ![解释器模式的 UML 图](/media/202203/2022-03-10_1355500.5866993745878373.png) ## 伪代码 解释器模式实现的关键是定义文法规则、设计终结符类与非终结符类、画出结构图,必要时构建语法树,其代码结构如下: ```java package net.biancheng.c.interpreter; //抽象表达式类 interface AbstractExpression { public void interpret(String info); //解释方法 } //终结符表达式类 class TerminalExpression implements AbstractExpression { public void interpret(String info) { //对终结符表达式的处理 } } //非终结符表达式类 class NonterminalExpression implements AbstractExpression { private AbstractExpression exp1; private AbstractExpression exp2; public void interpret(String info) { //非对终结符表达式的处理 } } //环境类 class Context { private AbstractExpression exp; public Context() { //数据初始化 } public void operation(String info) { //调用相关表达式类的解释方法 } } ``` ## 优点 1. 扩展性好。由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法。 2. 容易实现。在语法树中的每个表达式节点类都是相似的,所以实现其文法较为容易。 ## 缺点 1. 执行效率较低。解释器模式中通常使用大量的循环和递归调用,当要解释的句子较复杂时,其运行速度很慢,且代码的调试过程也比较麻烦。 2. 会引起类膨胀。解释器模式中的每条规则至少需要定义一个类,当包含的文法规则很多时,类的个数将急剧增加,导致系统难以管理与维护。 3. 可应用的场景比较少。在软件开发中,需要定义语言文法的应用实例非常少,所以这种模式很少被使用到。 ## 应用场景 前面介绍了解释器模式的结构与特点,下面分析它的应用场景。 1. 当语言的文法较为简单,且执行效率不是关键问题时。 2. 当问题重复出现,且可以用一种简单的语言来进行表达时。 3. 当一个语言需要解释执行,并且语言中的句子可以表示为一个抽象语法树的时候,如 XML 文档解释。 注意:解释器模式在实际的软件开发中使用比较少,因为它会引起效率、性能以及维护等问题。如果碰到对表达式的解释,在 Java 中可以用 Expression4J 或 Jep 等来设计。 ## 扩展 在项目开发中,如果要对数据表达式进行分析与计算,无须再用解释器模式进行设计了,Java 提供了以下强大的数学公式解析器:Expression4J、MESP(Math Expression String Parser) 和 Jep 等,它们可以解释一些复杂的文法,功能强大,使用简单。 ## 代码示例 ### Java 用解释器模式设计一个“韶粵通”公交车卡的读卡器程序。 说明:假如“韶粵通”公交车读卡器可以判断乘客的身份,如果是“韶关”或者“广州”的“老人” “妇女”“儿童”就可以免费乘车,其他人员乘车一次扣 2 元。 分析:本实例用“解释器模式”设计比较适合,首先设计其文法规则如下。 ``` <expression> ::= <city> 的 <person> <city> ::= 韶关 | 广州 <person> ::= 老人 | 妇女 | 儿童 ``` 然后,根据文法规则按以下步骤设计公交车卡的读卡器程序的类图。定义一个抽象表达式(Expression)接口,它包含了解释方法 interpret(String info)。 * 定义一个终结符表达式(Terminal Expression)类,它用集合(Set)类来保存满足条件的城市或人,并实现抽象表达式接口中的解释方法 interpret(Stringinfo),用来判断被分析的字符串是否是集合中的终结符。 * 定义一个非终结符表达式(AndExpressicm)类,它也是抽象表达式的子类,它包含满足条件的城市的终结符表达式对象和满足条件的人员的终结符表达式对象,并实现 interpret(String info) 方法,用来判断被分析的字符串是否是满足条件的城市中的满足条件的人员。 * 最后,定义一个环境(Context)类,它包含解释器需要的数据,完成对终结符表达式的初始化,并定义一个方法 freeRide(String info) 调用表达式对象的解释方法来对被分析的字符串进行解释。 ![“韶粵通”公交车读卡器程序的结构图](/media/202203/2022-03-10_1413330.38782303175825783.png) ```java package net.biancheng.c.interpreter; 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 元!"); } } ``` 输出: ```txt 您是韶关的老人,您本次乘车免费! 韶关的年轻人,您不是免费人员,本次乘车扣费 2 元! 您是广州的妇女,您本次乘车免费! 您是广州的儿童,您本次乘车免费! 山东的儿童,您不是免费人员,本次乘车扣费 2 元! ```
追风者
2022年3月11日 19:20
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
关于 MrDoc
觅思文档MrDoc
是
州的先生
开发并开源的在线文档系统,其适合作为个人和小型团队的云笔记、文档和知识库管理工具。
如果觅思文档给你或你的团队带来了帮助,欢迎对作者进行一些打赏捐助,这将有力支持作者持续投入精力更新和维护觅思文档,感谢你的捐助!
>>>捐助鸣谢列表
微信
支付宝
QQ
PayPal
Markdown文件
分享
链接
类型
密码
更新密码