- 解释器模式 interpreter pattern
解释器模式给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
特征:为了解释一种语言,而为语言创建的解释器。
解释器模式包含以下主要角色:
- 抽象表达式(Abstract Expression)角色:定义解释器的接口,约定解释器的解释操作,主要包含解释方法 interpret()。
- 终结符表达式(Terminal Expression)角色:是抽象表达式的子类,用来实现文法中与终结符相关的操作,文法中的每一个终结符都有一个具体终结表达式与之相对应。
- 非终结符表达式(Nonterminal Expression)角色:也是抽象表达式的子类,用来实现文法中与非终结符相关的操作,文法中的每条规则都对应于一个非终结符表达式。
- 环境(Context)角色:通常包含各个解释器需要的数据或是公共的功能,一般用来传递被所有解释器共享的数据,后面的解释器可以从这里获取这些值。
- 客户端(Client):主要任务是将需要分析的句子或表达式转换成使用解释器对象描述的抽象语法树,然后调用解释器的解释方法,当然也可以通过环境角色间接访问解释器的解释方法。
1.1 代码实现
下面以简单的加减乘除为例子实现解释器模式:
// 抽象角色 定义解释器
public interface Expression {
int interpret();
}
@AllArgsConstructor
public class NumberTerminal implements Expression {
private int number;
@Override
public int interpret() {
return this.number;
}
}
// 非终结表达式(抽象类)
@AllArgsConstructor
public abstract class NonTerminal implements Expression {
protected Expression left;
protected Expression right;
}
// 非终结表达式(加法)
public class PlusNonTerminal extends NonTerminal implements Expression {
public PlusNonTerminal(Expression left, Expression right) {
super(left, right);
}
@Override
public int interpret() {
return left.interpret() + right.interpret();
}
}
// 非终结表达式(减法)
public class MinusNonTerminal extends NonTerminal implements Expression {
public MinusNonTerminal(Expression left, Expression right) {
super(left, right);
}
@Override
public int interpret() {
return left.interpret() - right.interpret();
}
}
// 非终结表达式(乘法)
public class MclNonTerminal extends NonTerminal implements Expression {
public MclNonTerminal(Expression left, Expression right) {
super(left, right);
}
@Override
public int interpret() {
return left.interpret() * right.interpret();
}
}
// 非终结表达式(除法)
public class DivisionNonTerminal extends NonTerminal implements Expression {
public DivisionNonTerminal(Expression left, Expression right) {
super(left, right);
}
@Override
public int interpret() {
return left.interpret() / right.interpret();
}
}
// 计算器类(实现运算逻辑)
public class Cal {
private Expression left;
private Expression right;
private Integer result;
public Cal(String expression) {
this.parse(expression);
}
private Integer parse(String expression) {
// 获取表达式元素
String [] elements = expression.split(" ");
for (int i = 0; i < elements.length; i++) {
String element = elements[i];
// 判断是否是运算符号
if (OperatorUtils.isOperator(element)) {
// 运算符号的右边就是右终结符
right = new NumberTerminal(Integer.valueOf(elements[++i]));
//计算结果
result = OperatorUtils.getNonTerminal(left, right, element).interpret();
// 计算结果重新成为左终结符
left = new NumberTerminal(result);
} else {
left = new NumberTerminal(Integer.valueOf(element));
}
}
return result;
}
public Integer cal() {
return result;
}
}
// 操作工具类
public class OperatorUtils {
// 判断是不是非终结符
public static boolean isOperator(String symbol) {
return symbol.equals("+") || symbol.equals("-") || symbol.equals("*")|| symbol.equals("/");
}
// 简单工厂
public static NonTerminal getNonTerminal(Expression left, Expression right, String symbol) {
if (symbol.equals("+")) {
return new PlusNonTerminal(left, right);
} else if (symbol.equals("-")) {
return new MinusNonTerminal(left, right);
} else if (symbol.equals("*")) {
return new MclNonTerminal(left, right);
} else if (symbol.equals("/")) {
return new DivisionNonTerminal(left, right);
}
return null;
}
}
// 测试
// PS:此处进行的逻辑仅仅实现从左到右运算,并没有先乘除后加减的逻辑
public static void main(String[] args) {
System.out.println(new Cal("10 + 20 - 40 * 60").cal()); // -600
System.out.println(new Cal("20 + 50 - 60 * 2").cal()); // 20
}
1.2 Spring中的解释器模式
public static void main(String[] args) {
ExpressionParser expressionParser = new SpelExpressionParser();
org.springframework.expression.Expression expression = expressionParser.parseExpression("10 + 20 + 30 * 4");
Integer value = expression.getValue(Integer.class);
System.out.println(value); // 150
expression = expressionParser.parseExpression("(10+20+30)*4");
value = expression.getValue(Integer.class);
System.out.println(value); // 240
}
可以看到Spring中解释器写的是比较完善的,不仅有先乘除后加减和先括号进行运算的日常计算规则,而且对于空格也并没有要求,仅需要写出完整的表达式即可运算出来。
1.3 总结
适用场景:
- 一些重复出现的问题可以用一种简单的语言来进行表述。
- 一个简单语法需要解释的场景。
优点:
- 扩展性强:在解释器模式中由于语法是由很多类表示的,当语法规则更改时,只需修改相应的非终结符表达式即可;若扩展语法时,只需添加相应非终结符类即可。
- 增加了新的解释表达式的方式。
- 易于实现文法:解释器模式对应的文法应当是比较简单且易于实现的,过于复杂的语法并不适合使用解释器模式。
缺点:
- 语法规则较复杂时,会引起类膨胀。
- 执行效率比较低