解释器模式概述

解释器模式是一种使用频率相对较低但学习难度较大的设计模式,它用于描述如何使用面向对象语言构成一个简单的语言解释器。在某些情况下,为了更好地描述某一些特定类型的问题,我们可以创建一种新的语言,这种语言拥有自己的表达式和结构,即文法规则,这些问题的实例将对应为该语言中的句子。此时,可以使用解释器模式来设计这种新的语言。对解释器模式的学习能够加深我们对面向对象思想的理解,并且掌握编程语言中文法规则的解释过程。

解释器模式定义如下:

解释器模式(Interpreter Pattern):定义一个语言的文法,并且建立一个解释器来解释该语言中的句子,这里的“语言”是指使用规定格式和语法的代码。解释器模式是一种类行为型模式。

由于表达式可分为终结符表达式和非终结符表达式,因此解释器模式的结构与组合模式的结构有些类似,但在解释器模式中包含更多的组成元素,它的结构如图18-3所示:

图18-3  解释器模式结构图

在解释器模式结构图中包含如下几个角色:

● AbstractExpression(抽象表达式):在抽象表达式中声明了抽象的解释操作,它是所有终结符表达式和非终结符表达式的公共父类。

● TerminalExpression(终结符表达式):终结符表达式是抽象表达式的子类,它实现了与文法中的终结符相关联的解释操作,在句子中的每一个终结符都是该类的一个实例。通常在一个解释器模式中只有少数几个终结符表达式类,它们的实例可以通过非终结符表达式组成较为复杂的句子。

● NonterminalExpression(非终结符表达式):非终结符表达式也是抽象表达式的子类,它实现了文法中非终结符的解释操作,由于在非终结符表达式中可以包含终结符表达式,也可以继续包含非终结符表达式,因此其解释操作一般通过递归的方式来完成。

● Context(环境类):环境类又称为上下文类,它用于存储解释器之外的一些全局信息,通常它临时存储了需要解释的语句。

在解释器模式中,每一种终结符和非终结符都有一个具体类与之对应,正因为使用类来表示每一条文法规则,所以系统将具有较好的灵活性和可扩展性。对于所有的终结符和非终结符,我们首先需要抽象出一个公共父类,即抽象表达式类,其典型代码如下所示:

abstract class AbstractExpression {

public  abstract void interpret(Context ctx);

}

终结符表达式和非终结符表达式类都是抽象表达式类的子类,对于终结符表达式,其代码很简单,主要是对终结符元素的处理,其典型代码如下所示:

class TerminalExpression extends  AbstractExpression {

public  void interpret(Context ctx) {

//终结符表达式的解释操作

}

}

对于非终结符表达式,其代码相对比较复杂,因为可以通过非终结符将表达式组合成更加复杂的结构,对于包含两个操作元素的非终结符表达式类,其典型代码如下:

class NonterminalExpression extends  AbstractExpression {

private  AbstractExpression left;

private  AbstractExpression right;

public  NonterminalExpression(AbstractExpression left,AbstractExpression right) {

this.left=left;

this.right=right;

}

       public void interpret(Context ctx) {

              //递归调用每一个组成部分的interpret()方法

              //在递归调用时指定组成部分的连接方式,即非终结符的功能

       }     

}

除了上述用于表示表达式的类以外,通常在解释器模式中还提供了一个环境类Context,用于存储一些全局信息,通常在Context中包含了一个HashMap或ArrayList等类型的集合对象(也可以直接由HashMap等集合类充当环境类),存储一系列公共信息,如变量名与值的映射关系(key/value)等,用于在进行具体的解释操作时从中获取相关信息。其典型代码片段如下:

class Context {

private HashMap map = new HashMap();

public void assign(String key, String value) {

//往环境类中设值

}

public String  lookup(String key) {

//获取存储在环境类中的值

}

}

当系统无须提供全局公共信息时可以省略环境类,可根据实际情况决定是否需要环境类。

解释器模式实践

为了能够解释机器人控制指令,Sunny软件公司开发人员使用解释器模式来设计和实现机器人控制程序。针对五条文法规则,分别提供五个类来实现,其中终结符表达式direction、action和distance对应DirectionNode类、ActionNode类和DistanceNode类,非终结符表达式expression和composite对应SentenceNode类和AndNode类。

我们可以通过抽象语法树来表示具体解释过程,例如机器人控制指令“down run 10 and left move 20”对应的抽象语法树如图18-4所示:

图18-4   机器人控制程序抽象语法树实例

机器人控制程序实例基本结构如图18-5所示:

图18-5   机器人控制程序结构图

在图18-5中,AbstractNode充当抽象表达式角色,DirectionNode、ActionNode和DistanceNode充当终结符表达式角色,AndNode和SentenceNode充当非终结符表达式角色。完整代码如下所示:

[java] view plaincopy
  1. //注:本实例对机器人控制指令的输出结果进行模拟,将英文指令翻译为中文指令,实际情况是调用不同的控制程序进行机器人的控制,包括对移动方向、方式和距离的控制等
  2. import java.util.*;
  3. //抽象表达式
  4. abstract class AbstractNode {
  5. public abstract String interpret();
  6. }
  7. //And解释:非终结符表达式
  8. class AndNode extends AbstractNode {
  9. private AbstractNode left; //And的左表达式
  10. private AbstractNode right; //And的右表达式
  11. public AndNode(AbstractNode left, AbstractNode right) {
  12. this.left = left;
  13. this.right = right;
  14. }
  15. //And表达式解释操作
  16. public String interpret() {
  17. return left.interpret() + "再" + right.interpret();
  18. }
  19. }
  20. //简单句子解释:非终结符表达式
  21. class SentenceNode extends AbstractNode {
  22. private AbstractNode direction;
  23. private AbstractNode action;
  24. private AbstractNode distance;
  25. public SentenceNode(AbstractNode direction,AbstractNode action,AbstractNode distance) {
  26. this.direction = direction;
  27. this.action = action;
  28. this.distance = distance;
  29. }
  30. //简单句子的解释操作
  31. public String interpret() {
  32. return direction.interpret() + action.interpret() + distance.interpret();
  33. }
  34. }
  35. //方向解释:终结符表达式
  36. class DirectionNode extends AbstractNode {
  37. private String direction;
  38. public DirectionNode(String direction) {
  39. this.direction = direction;
  40. }
  41. //方向表达式的解释操作
  42. public String interpret() {
  43. if (direction.equalsIgnoreCase("up")) {
  44. return "向上";
  45. }
  46. else if (direction.equalsIgnoreCase("down")) {
  47. return "向下";
  48. }
  49. else if (direction.equalsIgnoreCase("left")) {
  50. return "向左";
  51. }
  52. else if (direction.equalsIgnoreCase("right")) {
  53. return "向右";
  54. }
  55. else {
  56. return "无效指令";
  57. }
  58. }
  59. }
  60. //动作解释:终结符表达式
  61. class ActionNode extends AbstractNode {
  62. private String action;
  63. public ActionNode(String action) {
  64. this.action = action;
  65. }
  66. //动作(移动方式)表达式的解释操作
  67. public String interpret() {
  68. if (action.equalsIgnoreCase("move")) {
  69. return "移动";
  70. }
  71. else if (action.equalsIgnoreCase("run")) {
  72. return "快速移动";
  73. }
  74. else {
  75. return "无效指令";
  76. }
  77. }
  78. }
  79. //距离解释:终结符表达式
  80. class DistanceNode extends AbstractNode {
  81. private String distance;
  82. public DistanceNode(String distance) {
  83. this.distance = distance;
  84. }
  85. //距离表达式的解释操作
  86. public String interpret() {
  87. return this.distance;
  88. }
  89. }
  90. //指令处理类:工具类
  91. class InstructionHandler {
  92. private String instruction;
  93. private AbstractNode node;
  94. public void handle(String instruction) {
  95. AbstractNode left = null, right = null;
  96. AbstractNode direction = null, action = null, distance = null;
  97. Stack stack = new Stack(); //声明一个栈对象用于存储抽象语法树
  98. String[] words = instruction.split(" "); //以空格分隔指令字符串
  99. for (int i = 0; i < words.length; i++) {
  100. //本实例采用栈的方式来处理指令,如果遇到“and”,则将其后的三个单词作为三个终结符表达式连成一个简单句子SentenceNode作为“and”的右表达式,而将从栈顶弹出的表达式作为“and”的左表达式,最后将新的“and”表达式压入栈中。                   if (words[i].equalsIgnoreCase("and")) {
  101. left = (AbstractNode)stack.pop(); //弹出栈顶表达式作为左表达式
  102. String word1= words[++i];
  103. direction = new DirectionNode(word1);
  104. String word2 = words[++i];
  105. action = new ActionNode(word2);
  106. String word3 = words[++i];
  107. distance = new DistanceNode(word3);
  108. right = new SentenceNode(direction,action,distance); //右表达式
  109. stack.push(new AndNode(left,right)); //将新表达式压入栈中
  110. }
  111. //如果是从头开始进行解释,则将前三个单词组成一个简单句子SentenceNode并将该句子压入栈中
  112. else {
  113. String word1 = words[i];
  114. direction = new DirectionNode(word1);
  115. String word2 = words[++i];
  116. action = new ActionNode(word2);
  117. String word3 = words[++i];
  118. distance = new DistanceNode(word3);
  119. left = new SentenceNode(direction,action,distance);
  120. stack.push(left); //将新表达式压入栈中
  121. }
  122. }
  123. this.node = (AbstractNode)stack.pop(); //将全部表达式从栈中弹出
  124. }
  125. public String output() {
  126. String result = node.interpret(); //解释表达式
  127. return result;
  128. }
  129. }

工具类InstructionHandler用于对输入指令进行处理,将输入指令分割为字符串数组,将第1个、第2个和第3个单词组合成一个句子,并存入栈中;如果发现有单词“and”,则将“and”后的第1个、第2个和第3个单词组合成一个新的句子作为“and”的右表达式,并从栈中取出原先所存句子作为左表达式,然后组合成一个And节点存入栈中。依此类推,直到整个指令解析结束。

编写如下客户端测试代码:

[java] view plaincopy
  1. class Client {
  2. public static void main(String args[]) {
  3. String instruction = "up move 5 and down run 10 and left move 5";
  4. InstructionHandler handler = new InstructionHandler();
  5. handler.handle(instruction);
  6. String outString;
  7. outString = handler.output();
  8. System.out.println(outString);
  9. }
  10. }

编译并运行程序,输出结果如下:

向上移动5再向下快速移动10再向左移动5

解释器模式总结

解释器模式为自定义语言的设计和实现提供了一种解决方案,它用于定义一组文法规则并通过这组文法规则来解释语言中的句子。虽然解释器模式的使用频率不是特别高,但是它在正则表达式、XML文档解释等领域还是得到了广泛使用。与解释器模式类似,目前还诞生了很多基于抽象语法树的源代码处理工具,例如Eclipse中的Eclipse AST,它可以用于表示Java语言的语法结构,用户可以通过扩展其功能,创建自己的文法规则。

      1. 主要优点

解释器模式的主要优点如下:

(1) 易于改变和扩展文法。由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法。

(2) 每一条文法规则都可以表示为一个类,因此可以方便地实现一个简单的语言。

(3) 实现文法较为容易。在抽象语法树中每一个表达式节点类的实现方式都是相似的,这些类的代码编写都不会特别复杂,还可以通过一些工具自动生成节点类代码。

(4) 增加新的解释表达式较为方便。如果用户需要增加新的解释表达式只需要对应增加一个新的终结符表达式或非终结符表达式类,原有表达式类代码无须修改,符合“开闭原则”。

      2. 主要缺点

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

(1) 对于复杂文法难以维护。在解释器模式中,每一条规则至少需要定义一个类,因此如果一个语言包含太多文法规则,类的个数将会急剧增加,导致系统难以管理和维护,此时可以考虑使用语法分析程序等方式来取代解释器模式。

(2) 执行效率较低。由于在解释器模式中使用了大量的循环和递归调用,因此在解释较为复杂的句子时其速度很慢,而且代码的调试过程也比较麻烦。

      3. 适用场景

在以下情况下可以考虑使用解释器模式:

(1) 可以将一个需要解释执行的语言中的句子表示为一个抽象语法树。

(2) 一些重复出现的问题可以用一种简单的语言来进行表达。

(3) 一个语言的文法较为简单。

(4) 执行效率不是关键问题。【注:高效的解释器通常不是通过直接解释抽象语法树来实现的,而是需要将它们转换成其他形式,使用解释器模式的执行效率并不高。】

转载于:https://www.cnblogs.com/vegetate/p/9997190.html

设计模式之---解释器模式相关推荐

  1. python实现解释器_Python设计模式之解释器模式

    解释器模式 对每个应用来说,至少有以下两种不同的用户分类. 基本用户:这类用户只希望能够凭直觉使用应用.他们不喜欢花太多时间配置或学习应用的内部.对他们来说,基本的用法就足够了. 高级用户:这些用户, ...

  2. java 设计模式之解释器模式(十九)

    java 设计模式之解释器模式①⑨ 定义 角色分析 使用场景 代码实现 躁动的心灵,不安的灵魂.在彷徨中挣扎,在迷失中探索.时光飞逝,努力不变. 设计模式学习,近期我会把23种设计模式都写成博客,敬请 ...

  3. 北风设计模式课程---解释器模式(Interpreter Pattern)

    北风设计模式课程---解释器模式(Interpreter Pattern) 一.总结 一句话总结: 不仅要通过视频学,还要看别的博客里面的介绍,搜讲解,搜作用,搜实例 设计模式都是对生活的抽象,比如用 ...

  4. 设计模式之解释器模式(Interpreter)摘录

    23种GOF设计模式一般分为三大类:创建型模式.结构型模式.行为模式. 创建型模式抽象了实例化过程,它们帮助一个系统独立于如何创建.组合和表示它的那些对象.一个类创建型模式使用继承改变被实例化的类,而 ...

  5. 螃蟹学PHP设计模式之解释器模式

    2019独角兽企业重金招聘Python工程师标准>>> 8.解释器模式 今天又迟到了一个小时,不过现在基本任务都做完了,螃蟹的效率还是挺高的.昨天一天跑各个工商局,发现大学生创业的扶 ...

  6. 设计模式复习-解释器模式

    #pragma once #include "stdafx.h" #include<map> #include<set> #include<list& ...

  7. 设计模式系列 - 解释器模式

    解释器模式通过实现一个表达式接口,从而能够以指定方式解析指定内容 介绍 解释器模式属于行为型模式,通过这种设计模式,我们可以定义一种特定的解释器来解释特定的业务场景,可以类比不同的编程语言的编译器需要 ...

  8. 设计模式:解释器模式(Interpreter)

    欢迎支持笔者新作:<深入理解Kafka:核心设计与实践原理>和<RabbitMQ实战指南>,同时欢迎关注笔者的微信公众号:朱小厮的博客. 欢迎跳转到本文的原文链接:https: ...

  9. Android设计模式之——解释器模式

    一.介绍 解释器模式(Interpreter Pattern)是一种用的比较少的行为型模式,其提供了一种解释语言的语法或表达式的方式,该模式定义了一个表达式接口,通过该接口解释一个特定的上下文.在这么 ...

  10. php 面向对象进阶,PHP面向对象进阶设计模式:解释器模式使用实例

    什么是解释器模式? 解释器设计模式用于分析一个实体的关键元素,并且针对每个元素都提供自己的解释或相应的动作. 解释器模式问题与解决方案 解释器设计模式是我们经常使用但未意识到的少数几个常见设计模式之一 ...

最新文章

  1. 【蓝桥java】进制与整除之天平秤重
  2. 机器学习成为未来趋势 北美未来将保持最大市场规模
  3. WebSocket 原理
  4. 大学python实训总结-千锋Python实训总结 学好基础才能走的更远
  5. Which of the following statement(s) equal(s) value 1 in C programming language?
  6. 【20090319-02】asp.net 操作 word(转)
  7. WebKit Loader模块介绍
  8. php socket keepalive,linux keepalive探测对应用层socket api的影响
  9. linux打开二进制文件后终端乱码处理
  10. 数据的实操与测试|附代码(全了)
  11. 版本管理器subversion的简单配置以及和apache的结合使用
  12. 访问虚拟机web应用程序
  13. 这一次,话筒给你:向自由软件之父 Richard M. Stallman 提问啦!
  14. 磁盘分区被格式化了,格式化之前的文件恢复方法
  15. M - Make Cents?
  16. tensorbook深度学习笔记本电脑
  17. 织梦主要文件夹目录及模板文件说明
  18. 免费多平台思维导图软件
  19. 备战数学建模35-时间序列预测模型
  20. 自动写作ai-自动写作神器

热门文章

  1. linux下运行class,在Linux下可用Wine安装和运行AIclass、希沃白板5
  2. mysql6.1 交叉编译_Armbian系统-mysql-connector-c++-1.1.8版本-交叉编译-安装
  3. 华为路由器时间同步_4G网络变WIFI,华为4G路由2 Pro让上网变得更简单
  4. 计算机组成原理解答题和计算题
  5. bzoj 4247: 挂饰(DP)
  6. 1621: [Usaco2008 Open]Roads Around The Farm分岔路口(记忆化搜索)
  7. 看了这篇c语言结构体精讲解,你难道还怕你不会吗?
  8. java发送接收组播(多播)数据包(UDP包)
  9. jQuery Mobile中选择select的data-*选项
  10. 事业单位考试高频考点二:经济