第十章、解释器模式
解释器模式是一种用的比较少的行为型模式,其提供了一种解释语言的语法或表达式的方式。但是它的使用场景确实很广泛,只是因为我们自己很少回去构造一个语言的文法,所以使用较少。
1.定义
给定一个语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。(其中语言就是我们需要解释的对象,文法就是这个语言的规律,解释器就是翻译机,通过文法来翻译语言。)
2.使用场景
1.如果某个简单的语言需要解释执行而且可以将该语言中的语句表示为一个抽象的语法树时可以考虑使用解释器模式。
2.在某些特定的领域出现不断重复的问题时,可以将该领域的问题转化为一种语法规则下的语句,然后构建解释器来解释该语句。
3.简单实现
我们使用解释器模式对“m+n+p”这个表达式进行解释,那么代表数字的m、n和p就可以看成终结符号,而“+”这个运算符号可以当做非终结符号。
TerminalExpression:终结符表达式,实现文法中与终结符有关的解释操作。文法中每个终结符都有一个具体的终结表达式与之对应。
NonterminalExpression :非终结符表达式,实现文法中与非终结符有关的解释操作。非终结符表达式根据逻辑的复杂程度而增加,原则上每个文法规则都对应一个非终结符表达式。
抽象的算数运算解释器
public abstract class ArithemticExpression {
/**
* 抽象的解析方法
* 具体的解析逻辑由具体的子类实现
*
* @return 解析得到具体的值
*/
public abstract int interpreter();
}
数字解释器
public class NumExpression extends ArithemticExpression{
private int num;
public NumExpression(int num){
this.num = num;
}
@Override
public int interpreter() {
return num;
}
}
运算符号解释器
public abstract class OperatorExpression extends ArithemticExpression{
protected ArithemticExpression exp1, exp2;
public OperatorExpression(ArithemticExpression exp1, ArithemticExpression exp2){
this.exp1 = exp1;
this.exp2 = exp2;
}
}
具体的加法运算符解释器
public class AdditionExpression extends OperatorExpression{
public AdditionExpression(ArithemticExpression exp1,
ArithemticExpression exp2) {
super(exp1, exp2);
}
@Override
public int interpreter() {
return exp1.interpreter() + exp2.interpreter();
}
}
处理解释器
public class Calculator {
//声明一个Stack栈储存并操作所有相关的解释器
private Stack<ArithemticExpression> mExpStack = new Stack<ArithemticExpression>();
public Calculator(String expression){
//声明两个ArithemticExpression类型的临时变量,储存运算符左右两边的数字解释器
ArithemticExpression exp1,exp2;
//根据空格分割表达式字符串(比如1 + 2 + 3 + 4)
String[] elements = expression.split(" ");
/*
* 遍历表达式元素数组
*/
for(int i = 0; i < elements.length; i++){
/*
* 判断运算符号
*/
switch (elements[i].charAt(0)) {
case ‘+‘:
//如果是加号,则将栈中的解释器弹出作为运算符号左边的解释器
exp1 = mExpStack.pop();
//同时将运算符号数组下标的下一个元素构造为一个数字解释器
exp2 = new NumExpression(Integer.parseInt(elements[++i]));
//通过上面的两个数字解释器构造加法运算解释器
mExpStack.push(new AdditionExpression(exp1, exp2));
break;
default:
/*
* 如果为数字,直接构造数字解释器并压入栈
*/
mExpStack.push(new NumExpression(Integer.valueOf(elements[i])));
break;
}
}
}
/**
* 计算结果
*
* @return 最终的计算结果
*/
public int calculate(){
return mExpStack.pop().interpreter();
}
}
调用
public class Client {
public static void main(String[] args) {
Calculator c = new Calculator("22 + 553 + 83 + 5");
System.out.println("计算结果:"+c.calculate());
}
}
结果:
计算结果:663
如果相加如减法的操作,在Calculator中加入相应判断即可:
public class SubtractionExpression extends OperatorExpression{
public SubtractionExpression(ArithemticExpression exp1,
ArithemticExpression exp2) {
super(exp1, exp2);
}
@Override
public int interpreter() {
return exp1.interpreter() - exp2.interpreter();
}
}
Calculator中加入:
case ‘-‘:
exp1 = mExpStack.pop();
exp2 = new NumExpression(Integer.parseInt(elements[++i]));
mExpStack.push(new SubtractionExpression(exp1, exp2));
break;
从上面可以看出解释器模式很灵活,他将复杂问题可以简单化、模块化、分离实现、解释执行。
4.Android源码中的模式实现
1.PackageParser
PackageParser是对AndroidManifest.xml配置文件进行读取的,具体原理参考:解析AndroidManifest原理
5.总结
1.优点
最大的优点使其灵活的扩展性,当我们想对文法规则进行扩展延伸时,只需要增加相应的非终结符解释器,并在构建抽象语法树时,使用到新增的解释器对象进行具体的解释即可,非常方便。
2.缺点
1.每个语法都要产生一个非终结符表达式,语法规则比较复杂时,就可能产生大量的类文件,为维护带来了非常多的麻烦。
2.解释器模式由于使用了大量的循环和递归,效率是个问题,特别是用于解析复杂、冗长的语法时,效率是难以忍受的。
6.参考
参考链接:解释器模式 详解