一、简介
解释器模式使用频率并不高,通常用来构建一个简单语言的语法解释器,它只在一些非常特定的领域被用到,比如编译器、规则引擎、正则表达式、sql解析等。
解释器模式是行为型设计模式之一,它的原始定义为:用于定义语言语法规则表示,并提供解释器来处理句子中的语法。
我们通过一个例子来解释下解释器模式,假设我们设计一个软件来进行加减计算,我们第一想法就是使用工具类,提供对应的加法和减法的工具方法。
public int add(int a,int b){
return a+b;
}
public int add(int a,int b,int c){
return a+b+c;
}
......
上面的形式比较单一、有限如果形式变化非常多,这就不符合要求,因为加法和减法运算,两个运算符数组可以有无限种合作方式,比如1+3+2+1-5....
解释器模式描述了如何为简单的语言定义一个文法,如何在该语言中表示一个句子,以及如何解释这些句子。
二、结构
解释器模式由以下几种角色组成:
①抽象表达式角色:定义解释器的接口,约定解释器的操作,主要包含解释方法interoret
②终结符表达式角色:用来实现文发中与终结符相关的操作,文法中的每一个终结符都有一个具体的终结符表达式与之对应
③非终结符表达式:用来实现文法中与非终结符相关的操作,文法中每条规则都对应一个非终结符表达式
④上下文类:通常包含各个解释器需要的数据或者是公共的功能,一般用来传递所有解释器共享的数据。
三、实现
我们定义一个进行加减乘除计算的语言,语法规则如下:
①运算符只包含加减乘除,并且没有优先级概念
②表达式中,先写数字后写运算符,空格隔开
// 表达式接口
public interface Expression {
int interpret();
}
// 加法解释器
public class AddExpression implements Expression {
private Expression left;
private Expression right;
public AddExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}
@Override
public int interpret() {
return left.interpret() + right.interpret();
}
}
// 减法解释器
public class SubtractExpression implements Expression {
private Expression left;
private Expression right;
public SubtractExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}
@Override
public int interpret() {
return left.interpret() - right.interpret();
}
}
// 乘法解释器
public class MultiplyExpression implements Expression {
private Expression left;
private Expression right;
public MultiplyExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}
@Override
public int interpret() {
return left.interpret() * right.interpret();
}
}
// 除法解释器
public class DivideExpression implements Expression {
private Expression left;
private Expression right;
public DivideExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}
@Override
public int interpret() {
try {
return left.interpret() / right.interpret();
} catch (ArithmeticException e) {
System.out.println("除数不能为0");
return -1;
}
}
}
// 数字解释器
public class NumberExpression implements Expression {
private int number;
public NumberExpression(int number) {
this.number = number;
}
@Override
public int interpret() {
return number;
}
}
现在我们可以使用这些类来解析四则运算表达式了。例如,对于表达式 “1 + 2 * 3 - 4 / 2”我们可以使用以下代码来解析该表达式:
Expression expression = new SubtractExpression(
new AddExpression(
new NumberExpression(1),
new MultiplyExpression(
new NumberExpression(2),
new NumberExpression(3)
)
),
new DivideExpression(
new NumberExpression(4),
new NumberExpression(2)
)
);
int result = expression.interpret();
System.out.println("计算结果为:" + result);
四、总结
优点
①易于改变可扩展文法,因为在解释器模式中实用类来表示语言的文法规则的,因此可以通过继承等机制改变或扩展文法,每一个文法规则都可以表示为一个类,因此我们可以快速的实现一个迷你的语言
②实现文法比较容易,在抽像语法术中每一个表达式节点的实现方式都是相似的,这些代码不特别复杂
③增加新的解释表达式比较方便,如果用户增加新的解释表达式,只需要对应增加一个新的表达式类就可以,原有的表达式不需修改,符合开闭原则
缺点
①对于复杂文法难以维护,在解释器中,一条规则至少要定义一个类,因此一个语言中有太多的文法规则,那么就会使类的个数急剧增加,导致系统的维护难以管理。
②执行效率低,在解释器模式中,大量的使用循环和递归调用,所以复杂的句子执行起来比较繁琐
使用场景
①当语言的文法比较简单时,并且执行的效率不是关键问题的时候
②当问题重复出现,且可以用一种简单的语言来表达
③当一个语言要解释执行,并且语言中的句子可以表示为一个抽象的语法树的时候。