顿搜
解释器模式(Interpreter)——23种设计模式之行为型模式
在软件构建过程中,如果某一特定领域的问题比较复杂,类似的模式不断重复出现,如果使用普遍的编程方式来实现将面临非常频繁的变化。在这种情况下,将特定领域的问题表达为某种语法规则下的句子,然后构建一个解释器来解释这样的句子,从而达到解决问题的目的。
如果一种特定类型的问题发生的频率足够高, 那么可能就值得将该问题的各个实例表述为一个简单语言中的句子。这样就可以构建一个解释器,该解释器通过解释 这些句子来解决该问题。解释器模式描述了如何为简 单的语言定义一个文法并解释。
解释器模式描述了如何构成一个简单的语言解释器,主要应用在使用面向对象语言开发编译器中,当文法简单、效率不是关键问题的时候效果最好。
在实际应用中,可能很少碰到去构造一个语言 的文法的情况。
一、类图表示
解释器模式涉及到AbstractExpression(抽象表达式),TerminalExpression(终结符表达式),NonterminalExpression(非终结符表达式),Context(环境类),Client(客户类)
- ①、AbstractExpression(抽象表达式):声明一个抽象的解释操作,这个接口为抽象语法树中所有的节点所共享
- ②、TerminalExpression(终结符表达式):实现与文法中的终结符相关联的解释操作,一个句子中的每一个终结符需要该类的一个实例
- ③、NonterminalExpression(非终结符表达式):对文法中的每一条规则R ::= R1R2. . . Rn都需要一个NonterminalExpression类,为从R1到Rn的每个符号都维护一个AbstractExpression类型的实例变量为文法中的非终结符实现Interpret操作,一般要递归
地调用表示R1到Rn的那些对象的解释操作 - ④、Context(环境类):包含解释器之外的一些全局信息
- ⑤、Client(客户类):建造一个语法抽象树,调用interpret()
二、代码示例
//上下文(环境)角色,使用HashMap来存储变量对应的数值
class Context{
private Map valueMap = new HashMap();
public void addValue(Variable x , int y){
Integer yi = new Integer(y);
valueMap.put(x , yi);
}
public int LookupValue(Variable x){
int i = ((Integer)valueMap.get(x)).intValue();
return i ;
}
}//抽象表达式角色,也可以用接口来实现
abstract class Expression{
public abstract int interpret(Context con);
}//终结符表达式角色
class Constant extends Expression{
private int i ;
public Constant(int i){
this.i = i;
}
public int interpret(Context con){
return i ;
}
}class Variable extends Expression{
public int interpret(Context con){
//this为调用interpret方法的Variable对象
return con.LookupValue(this);
}
}//非终结符表达式角色
class Add extends Expression{
private Expression left ,right ;
public Add(Expression left , Expression right){
this.left = left ;
this.right= right ;
}
public int interpret(Context con){
return left.interpret(con) + right.interpret(con);
}
}class Subtract extends Expression{
private Expression left , right ;
public Subtract(Expression left , Expression right){
this.left = left ;
this.right= right ;
}
public int interpret(Context con){
return left.interpret(con) - right.interpret(con);
}
}class Multiply extends Expression{
private Expression left , right ;
public Multiply(Expression left , Expression right){
this.left = left ;
this.right= right ;
}
public int interpret(Context con){
return left.interpret(con) * right.interpret(con);
}
}class Division extends Expression{
private Expression left , right ;
public Division(Expression left , Expression right){
this.left = left ;
this.right= right ;
}
public int interpret(Context con){
try{
return left.interpret(con) / right.interpret(con);
}catch(ArithmeticException ae){
System.out.println("被除数为0!");
return -11111;
}
}
}//测试程序,计算 (a*b)/(a-b+2)
public class Test{
private static Expression ex ;
private static Context con ;
public static void main(String[] args){
con = new Context();
//设置变量、常量
Variable a = new Variable();
Variable b = new Variable();
Constant c = new Constant(2);
//为变量赋值
con.addValue(a , 5);
con.addValue(b , 7);
//运算,对句子的结构由我们自己来分析,构造
ex = new Division(new Multiply(a , b), new Add(new Subtract(a , b) , c));
System.out.println("运算结果为:"+ex.interpret(con));
}
}三、模式分析
每一非终结符表达式节点定义相应子表达式的解释操作。而各终结符表达式的解释操作构成了递归的基础。
- ①、每一节点的解释操作用上下文来存储和访问解释器的状态。
- ②、抽象语法树描述了如何构成一个复杂的句子,通 过对抽象语法树的分析,可以识别出语言中的终结符和非终结符类。
- ③、在解释器模式中,每一种终结符和非终结符都有 一个具体类与之对应,正因为使用类来表示每一 个语法规则,使得系统具有较好的扩展性和灵活 性。
四、优缺点分析
1、优点
- ①、易于改变和扩展文法,也易于实现文法
- ②、使用Interpreter模式来表示文法规则,从而可以使用 面向对象技巧来方便地“扩展”文法。
- ③、解释器模式提供了一个简单的方式来执行语法,而且容易修改或者扩展语法。
- ④、一般系统中很多类使用相似的语法,可以使用一个解释器来代替为每一个规则实现一个解释器。而且在解释器中不同的规则是由不同的类来实现的,这样使得添加一个新的语法规则变得简单。
2、缺点
- ①、采用递归调用方法,复杂的文法难以维护,增加了新的解释表达式的方式
- ②、会引起类膨胀。
- ③、每一个规则要对应一个处理类,而且这些类还要递归调用抽象表达式 角色。调试复杂。
- ④、使用大量循环与递归,效率是问题,尤其是一用于解析复杂/冗长的语法时,效率难以忍受。
五、模式间关系
1、与组合模式的关系
- ①、抽象语法树是一个组合模式的实例
2、与享元模式的关系
- ①、享元模式说明了如何在抽象语法树中共享终结符
3、与迭代器模式的关系
- ①、解释器可用一个迭代器遍历该结构
4、与访问者模式的关系
- ①、可用来在一个类中维护抽象语法树中的 各节点的行为
六、应用场景
当有一个语言需要解释执行,并且你可将该语言中的句 子表示为一个抽象语法树时,可使用解释器模式。
- ②、当该文法简单,效率不是一个关键问题时,系统有一个简单的语言可供解释时该模式效果最好
- ③、一些重复发生的问题可以用这种简单的语言表达时
- ④、解释器模式在使用面向对象语言实现的编译器中得到 了广泛应用,如smalltalk编译器。
七、总结
一般用于开发编译器中,特定领域问题发生频率特别高。
- ①、Interpreter模式的应用场合是interpreter模式应用中的 难点,只有满足"业务规则频繁变化,且类似的模式 不断重复出现,并且容易抽象为语法规则的问题"才 适合使用Interpreter模式
- ②、Interpreter模式比较适合简单的文法表示,对于复杂 的文法表示,Interpreter模式会产生比较大的类层次 结构,需要求助于语法分析生成器这样的标准工具
- ③、在最宽泛的概念下,几乎每个使用组合模式的系统都使用了解释器模式。但一般只有在用一个类层次来定 义某个语言时,才强调使用解释器模式。
- ④、解释器模式在实际的系统开发中用得较少,会引起效 率、性能及维护等问题,一般多在大中型的框架型项 目中使用,如一些数据分析工具、报表设计工具、科 学计算工具等,但现在有很多开源的解析工具包可直接使用。
