> 文章列表 > Windows环境下实现设计模式——解释器模式(JAVA版)

Windows环境下实现设计模式——解释器模式(JAVA版)

Windows环境下实现设计模式——解释器模式(JAVA版)

我是荔园微风,作为一名在IT界整整25年的老兵,今天总结一下Windows环境下如何编程实现解释器模式(设计模式)。

不知道大家有没有这样的感觉,看了一大堆编程和设计模式的书,却还是很难理解设计模式,无从下手。为什么?因为你看的都是理论书籍。

我今天就在Windows操作系统上安装好JAVA的IDE编程工具,并用JAVA语言来实现一个解释器模式,真实的实现一个,你看懂代码后,自然就明白了。

解释器模式Interpreter Pattern  (行为型设计模式)

定义:给定一个语言,定义这个语言的文法表示,并且建立一个解释器来解释该语言中的句子,这里的“语言”是指使用规定格式和语法的代码。

上面定义听懂了吗?莫名其妙看不懂对吧。所以我们还是来看看实现生活中的例子。

解释器模式主要用于描述如何构成一个简单的语言解释器,主要应用于使用面向对象语言开发的解释器的设计。当需要开发一个新的语言时可以考虑使用解释器模式。所以这种解释器模式用的比较少,但是要理解它却很难,是最难的一种行为型设计模式。

有时候我们希望输入一个计算公式给计算机就能得到结果,但是计算机并不认识加号和减号,它怎么计算呢?能用一些简单的语言来实现一些特定的操作吗?我们必须自己定义一套文法规则来实现对这些语句的解释,即设计一个自定义语言。

在实际开发中,这些简单的自定义语言可以基于现有的编程语言来设计,如果所基于的编程语言是面向对象语言,此时可以使用解释器模式来实现自定义语言。我们只要向计算机输入一个句子或文件,它就能够按照预先定义的文法规则来对句子或文件进行解释,从而实现相应的功能。如果能做到这一点不是很好?

在前面所提到的加法/减法解释器中,每一个输入表达式,例如“1 + 2 + 3”,都包含了三个语言单位,可以使用如下文法规则来定义:

expression ::= value | operationoperation ::= expression '+' expression | expression '-'  expressionvalue ::= an integer //一个整数值

现在我们来看一下上面三条分别是什么意思。

第一条表示表达式的组成方式,其中value和operation是后面两个语言单位的定义,每一条语句所定义的字符串如operation和value称为语言构造成分或语言单位,符号“::=”表示“定义为”的意思,其左边的语言单位通过右边来进行说明和定义,语言单位对应终结符表达式和非终结符表达式。如本规则中的operation是非终结符表达式,它的组成元素仍然可以是表达式,可以进一步分解,而value是终结符表达式,它的组成元素是最基本的语言单位,不能再进行分解。在文法规则定义中可以使用一些符号来表示不同的含义,如使用“|”表示或,使用“{”和“}”表示组合,使用“*”表示出现0次或多次等,其中使用频率最高的符号是表示“或”关系的“|”。在解释器模式中还可以通过一种称之为抽象语法树(Abstract Syntax Tree, AST)的图形方式来直观地表示语言的构成,每一棵抽象语法树对应一个语言实例,如加法/减法表达式语言中的语句“1+ 2 + 3”,可以通过如图所示抽象语法树来表示:

在该抽象语法树中,可以通过终结符表达式value和非终结符表达式operation组成复杂的语句,每个文法规则的语言实例都可以表示为一个抽象语法树,即每一条具体的语句都可以用类似的抽象语法树来表示,在图中终结符表达式类的实例作为树的叶子节点,而非终结符表达式类的实例作为非叶子节点,它们可以将终结符表达式类的实例以及包含终结符和非终结符实例的子表达式作为其子节点。抽象语法树描述了如何构成一个复杂的句子,通过对抽象语法树的分析,可以识别出语言中的终结符类和非终结符类。

由于表达式可分为终结符表达式和非终结符表达式,因此解释器模式的结构如图所示:

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

 AbstractExpression(抽象表达式):在抽象表达式中声明了抽象的解释操作,它是所有终结符表达式和非终结符表达式的公共父类。TerminalExpression(终结符表达式):终结符表达式是抽象表达式的子类,它实现了与文法中的终结符相关联的解释操作,在句子中的每一个终结符都是该类的一个实例。通常在一个解释器模式中只有少数几个终结符表达式类,它们的实例可以通过非终结符表达式组成较为复杂的句子。NonterminalExpression(非终结符表达式):非终结符表达式也是抽象表达式的子类,它实现了文法中非终结符的解释操作,由于在非终结符表达式中可以包含终结符表达式,也可以继续包含非终结符表达式,因此其解释操作一般通过递归的方式来完成。Context(环境类):环境类又称为上下文类,它用于存储解释器之外的一些全局信息,通常它临时存储了需要解释的语句。

具体代码

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

public abstract class AbstractExpression {public  abstract void interpret(Context ctx);
}

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

public class TerminalExpression extends  AbstractExpression {public  void interpret(Context ctx) {//终结符表达式的解释操作}
}

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

public 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)等,用于在进行具体的解释操作时从中获取相关信息。其代码片段如下:

public class Context {private HashMap<String,String> map = new HashMapp<String,String>();public void assign(String key, String value) {//往环境类中设值map.put(key,value);}public String  lookup(String key) {//获取存储在环境类中的值return map.get(key);   }
}

环境类Context的对象通常作为参数被传递到所有表达式的解释方法interpret()中,可以在环境类对象中存储和访问表达式解释器的状态,向表达式解释器提供一些全局的、公共的数据,此外还可以在环境类中增加一些所有表达式解释器共有的功能,以减轻解释器的职责。当系统无须提供全局公共信息时可以省略环境类,根据实际情况决定是否需要环境类。

应用实例

好,我们现在来一个实例,可以加深理解。我们今天来看看实例是什么

我们来讲讲无人驾驶汽车。

汽车公司要开发一套无人驾驶汽车的控制程序,在该控制程序中包含一些简单的控制指令,每一个指令对应一个表达式(expression),该表达式可以是简单表达式也可以是复合表达式,每一个简单表达式由移动方向(direction),移动方式(action)和移动距离(distance)三部分组成,其中移动方向包括前(front)、后(back)、左(left)、右(right);移动方式包括移动(move)和快速移动(run);移动距离为一个正整数。两个表达式之间可以通过与(and)连接,形成复合(composite)表达式。用户通过对图形化的设置界面进行操作可以创建一个控制指令,机器人在收到指令后将按照指令的设置进行移动,例如输入控制指令:front move 5,则“向前移动5个单位”;输入控制指令:back  run 10 and left move 20,则“向后快速移动10个单位再向左移动20个单位”。

根据上述需求描述,用形式化语言来表示该简单语言的文法规则如下:

expression ::= direction action distance | composite //表达式
composite ::= expression 'and' expression //复合表达式
direction ::= 'front' | 'back' | 'left' | 'right' //移动方向
action ::= 'move' | 'run' //移动方式
distance ::= an integer //移动距离