Java设计模式之解释器模式

前言

解释器模式用于描述如何构成一个简单的语言解释器,主要应用于使用面向对象语言开发的解释器的设计。当需要开发一个新的语言时可以考虑使用解释器模式。

正文

在某些情况下,为了更好地描述某些特定类型的问题可以创建一种新的语言,这种语言拥有自己的表达式和结构,即文法规则,这些问题的实例将对应为该语言中的句子,此时可以使用解释器模式来设计这种新的语言。

一、定义

定义如下:

解释器模式: 给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
Interperter Pattern: Given a language,define a representation for its grammar along with an interpreter
that uses the representation to interpret sentences in the language.

在解释器模式的定义中所指的“语言”是使用规定格式和语法的代码,解释器模式是一种类行为型模式。

二、文法规则和抽象语法树

如“1+2+3-4+1”都包含了3个语言单位,可以使用以下文法规则来定义:

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

该文法规则包含3条语句,第一条表示表达式的组成方式,其中value和operation是后面两个语言单位的定义,每一条语句所定义的字符串(如operation和value)称为语言构造成分或语言单位,符号“:: ="是”定义为“的意思,其左边的语言单位通过右边来进行说明和定义,语言单位对应终结符表达式和非终结符表达式。例如本规则中operatIon是非终结符表达式,它的组成元素仍然可以是表达式,可以进一步分解,而value是终结符表达式,它的组成元素是最基本的语言单位,不能再进行分解。

在文法规则定义中可以使用一些符号来表示不同的含义,如使用“|”表示或、使用“{”和“}”表示组合、使用“*”表示出现0次或多次等。

除了使用文法规则来定义一个语言外,在解释器模式中还可以通过一种称为抽象语法树(Abstract Syntax Tree,AST)的图形方式来直观地表示语言的构成,每一棵抽象语法树对应一个语言实例。抽象语法树描述了如何构成一个复杂的句子,通过对抽象语法树的分析可以识别出语言中的终结符类和非终结符类。

三、结构

(1)AbstractExpression(抽象表达式):在抽象表达式中声明了抽象的解释操作,它是所有终结符表达式和非终结符表达式的公共父类。
(2)TerminalExpression(终结符表达式):终结符表达式是抽象表达式的子类,它实现了与文法中的终结符相关联的解释操作,在句子中的每一个终结符都是该类的一个实例。通常在一个解释器模式中只有少数几个终结符表达式类,它们的实例可以通过非终结符表达式组成较为复杂的句子。
(3)NonterminalExpression(非终结符表达式):非终结符表达式也是抽象表达式的子类,它实现了文法中非终结符的解释操作,由于在非终结符表达式中可以包含终结符表达式,也可以继续包含非终结符表达式,因此其解释操作一般通过递归的方式完成。
(4)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()方法
		//在递归调用时指定组成部分的连接方式,即非终结符的功能
	}
}

环境类,典型代码如下:

public class Context{
	private HashMap<String,String> map = new HashMap<String,String>();

	public void assign(String key,String value){
		//往环境类中设值
		map.put(key,value);
	}

	public String lookup(String key){
		//获取存储在环境类中的值
		return map.get(key);
	}
}

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

五、解释器模式优缺点与适用环境

优点

(1)易于改变和扩展文法。由于在解释器模式中使用类表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法。
(2)每一条文法规则都可以表示为一个类,因此可以方便地实现一个简单的语言。
(3)实现文法较为容易。
(4)增加新的解释表达式较为方便。如果用户要增加新的解释表达式只需要对应增加一个新的终结符表达式或非终结符表达式类,原有表达式类代码无须修改,符合开闭原则。

缺点

(1)对于复杂文法难以维护。在解释器模式中每一条规则至少需要定义一个类,因此如果一个语言包含太多文法规则,类的个数将会急剧增加,导致系统难以管理和维护,此时可以考虑使用语法分析程序等方式来取代解释器模式。
(2)执行效率低。由于在解释器模式中采用了大量的循环和递归调用,因此在解释较为复杂的句子时其速度很慢,而且代码的调试过程也比较麻烦。

适用环境

(1)可以将一个需要解释执行的语言中的句子表示为一棵抽象语法树。
(2)一些重复出现的问题可以用一种简单的语言进行表达。
(3)一个语言的文法较为简单。对于复杂的文法,解释器模式中的文法类层次结构将变得很庞大而无法管理,此时最好使用语法分析程序生成器。
(4)执行效率不是关键问题。高效的解释器通常不是通过直接解释抽象语法树来实现的,而是需要将它们转换成其他形式,使用解释器模式的执行效率并不高。

以上文字,大量摘抄自《Java设计模式》一书,由刘伟老师编著,故本文应当列入转载一类,有兴趣的朋友可以直接阅读原书。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值