化繁为简的翻译机——解释器模式
解释器模式的定义
给定一个语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。其实就是自己定义一个规则,将一个语言解释清楚。
解释器模式的使用场景
- 如果某个简单的语言需要解释执行而且可以将该语言中的语句表示为一个抽象语法树时,可以考虑使用解释器模式。
- 在某些特定的领域出现不断重复的问题时,可以将该领域的问题转化为一种语法规则下的语句,然后构建解释器来解释该语句。
解释器模式的实例
解释器模式在实际运用上相对来说要少很多,因为我们很少自己器构造一个语言,但是它的应用范围相当广泛。我们来对一个算术表达式运用解释器模式,如“m + n + p”,m、n、p代表数字,‘+’代表运算符。
抽象算术运算解释器
ArithmeticExpression.class
public abstract class ArithmeticExpression {
/**
* description: 抽象的解析方法
* 具体的解析逻辑由具体的子类实现
*/
public abstract int interpret();
}
数字解释器
NumExpression.class
public class NumExpression extends ArithmeticExpression {
private int num;
public NumExpression(int num) {
this.num = num;
}
@Override
public int interpret() {
return 0;
}
}
运算符号抽象解释器
OperatorExpression.class
public abstract class OperatorExpression extends ArithmeticExpression{
//声明两个成员变量存储运算符号两边的数字解释器
protected ArithmeticExpression expression1, expression2;
public OperatorExpression(ArithmeticExpression expression1, ArithmeticExpression expression2) {
this.expression1 = expression1;
this.expression2 = expression2;
}
}
加法运算解释器
AdditionExpression.class
public class AdditionExpression extends OperatorExpression {
public AdditionExpression(ArithmeticExpression expression1, ArithmeticExpression expression2) {
super(expression1, expression2);
}
@Override
public int interpret() {
return expression1.interpret() + expression2.interpret();
}
}
减法运算解释器
SubtractionExpression.class
public class SubtractionExpression extends OperatorExpression {
public SubtractionExpression(ArithmeticExpression expression1, ArithmeticExpression expression2) {
super(expression1, expression2);
}
@Override
public int interpret() {
return expression1.interpret() - expression2.interpret();
}
}
处理与解释
Calculator.class
public class Calculator {
//声明一个Stack栈存储并操作所有相关的解释器
private Stack<ArithmeticExpression> expressionStack = new Stack<>();
public Calculator(String expression) {
//声明两个ArithmeticExpression类型的临时变量,存储运算符左右两边的数字解释器
ArithmeticExpression expression1, expression2;
//根据空格分隔表达式字符串
String[] elements = expression.split(" ");
//循环遍历表达式元素数组
for (int i = 0; i < elements.length; i++) {
//判断运算符
switch (elements[i].charAt(0)) {
case '+'://如果是加号
//则将占中的解释器弹出作为运算符号左边的解释器
expression1 = expressionStack.pop();
//同时将运算符号数组下标下一个元素构造为一个数字解释器
expression2 = new NumExpression(Integer.valueOf(elements[++i]));
//通过上面两个数字解释器构造加法运算解释器
expressionStack.push(new AdditionExpression(expression1, expression2));
break;
case '-'://如果是减号
expression1 = expressionStack.pop();
expression2 = new NumExpression(Integer.valueOf(elements[++i]));
expressionStack.push(new SubtractionExpression(expression1, expression2));
break;
default:
//如果不是运算符则为数字,若是数字,直接构造数字解释器并压入栈
expressionStack.push(new NumExpression(Integer.valueOf(elements[i])));
break;
}
}
}
/**
* description: 计算结果
*/
public int calculate() {
return expressionStack.pop().interpret();
}
}
调用:
Calculator calculator = new Calculator("153 + 118 - 555 + 3598 - 597 - 66");
Log.i(TAG, "result = " + calculator.calculate());
将一条具体的文法通过一个解释器解释,把复杂的文法规则分离为简单的功能进行解释,最后将其组合一颗抽象语法树解释执行,至此,我们可以看到解释器模式的原理与本质:将复杂的问题简单化、模块化、分离实现、解释执行。
小结
优点
最大的有点就是其灵活的扩展性,当我们想对文法规则进行扩展延伸时,只需要增加解释器。
缺点
每一条文法都有可以对应至少一个解释器,其会生成大量的类,导致后期维护困难。同时对于过于复杂的文法,构建其抽象语法树会显得异常烦琐。