行为型模式——解释器模式
2020-08-23 本文已影响0人
Doooook
解释器模式(Interpreter Pattern):定义一个语言的文法,并且建立一个解释器来解释该语言中的句子,这里的 “语言” 是指使用规定格式和语法的代码。解释器模式是一种类行为型模式。
解释器模式由以下类组成:
- Context(环境):Context用于封装解释器的全局信息,所有具体的解释器均需访问Context。
- AbstractExpression(抽象表达式):一个抽象类或接口,声明执行的解释方法,由所有具体的解释器实现。
- TerminalExpression(终结符表达式):一种解释器类,实现与语法的终结符相关的操作。终结符表达式必须始终被实现和实例化,因为它表示表达式的结尾。
- NonTerminalExpression(非终结符表达式):这是实现语法的不同规则或符号的类。对于每一个语法都应该创建一个类。
这里我们选择一个简单的语法作为例子。我们将应用解释器模式来解析带有一个变量的简单函数f(x),实现简单的加、减、乘。
为了简单,我们选择逆波兰表示法,这是一种在运算符末尾添加操作数的表示法。1+2变为1 2 +,(1+2)*3变为1 2 + 3 *。优点是不再需要括号,因此它简化了任务。
Interpreter解释器接口
/**
* @author: Jay Mitter
* @date: 2020-08-23 20:54
* @description: 解释器
* 使用逆波兰表示法(后缀表达式,优点是不再需要括号),如:
* 1 2 +(等同于1+2);
* 1 2 + 3 *(等同于(1+2)*3)
*/
public interface Interpreter {
/**
* 解释器
* @return
*/
float interpret();
}
非终结符表达式,对整数进行解释
/**
* @author: Jay Mitter
* @date: 2020-08-23 21:07
* @description: 非终结符表达式,对整数进行解释
*/
public class NumberInterpreter implements Interpreter {
private float number;
public NumberInterpreter(float number) {
this.number = number;
}
@Override
public float interpret() {
return number;
}
}
终结符表达式,对加法进行解释
/**
* @author: Jay Mitter
* @date: 2020-08-23 20:55
* @description: 终结符表达式,对加法进行解释
*/
public class PlusInterpreter implements Interpreter {
/**
* 表达式的左边
*/
private Interpreter left;
/**
* 表达式的右边
*/
private Interpreter right;
public PlusInterpreter(Interpreter left, Interpreter right) {
this.left = left;
this.right = right;
}
@Override
public float interpret() {
return left.interpret() + right.interpret();
}
}
终结符表达式,对减法进行解释
/**
* @author: Jay Mitter
* @date: 2020-08-23 20:55
* @description: 终结符表达式,对减法进行解释
*/
public class MinusInterpreter implements Interpreter {
/**
* 表达式的左边
*/
private Interpreter left;
/**
* 表达式的右边
*/
private Interpreter right;
public MinusInterpreter(Interpreter left, Interpreter right) {
this.left = left;
this.right = right;
}
@Override
public float interpret() {
return right.interpret() - left.interpret();
}
}
终结符表达式,对乘法进行解释
/**
* @author: Jay Mitter
* @date: 2020-08-23 21:07
* @description: 终结符表达式,对乘法进行解释
*/
public class MultiInterpreter implements Interpreter {
/**
* 表达式的左边
*/
private Interpreter left;
/**
* 表达式的右边
*/
private Interpreter right;
public MultiInterpreter(Interpreter left, Interpreter right) {
this.left = left;
this.right = right;
}
@Override
public float interpret() {
return left.interpret() * right.interpret();
}
}
操作解释器,操作语法树
/**
* @author: Jay Mitter
* @date: 2020-08-23 20:58
* @description: 操作解释器,操作语法树
*/
public class Evaluator {
public float evaluate(String expression) {
Stack<Interpreter> stack = new Stack<>();
float result = 0;
for (String token : expression.split(" ")) {
if (isOperator(token)) {
Interpreter exp = null;
if ("+".equals(token)) {
exp = stack.push(new PlusInterpreter(stack.pop(), stack.pop()));
} else if ("-".equals(token)) {
exp = stack.push(new MinusInterpreter(stack.pop(), stack.pop()));
} else if ("*".equals(token)) {
exp = stack.push(new MultiInterpreter(stack.pop(), stack.pop()));
}
if (null != exp) {
result = exp.interpret();
stack.push(new NumberInterpreter(result));
}
} else if (isNumber(token)) {
stack.push(new NumberInterpreter(Float.parseFloat(token)));
}
}
return result;
}
private boolean isOperator(String token) {
return "+".equals(token) || "-".equals(token) || "*".equals(token);
}
private boolean isNumber(String token) {
try {
Float.parseFloat(token);
return true;
} catch (NumberFormatException e) {
return false;
}
}
}
测试:
/**
* 行为型模式——解释器模式
*/
@Test
public void testBehaviorInterpreter() {
Evaluator evaluator = new Evaluator();
// 1+2=3
float result = evaluator.evaluate("1 2 +");
System.out.println(result);
// 4+3-2=5
result = evaluator.evaluate("4 3 + 2 -");
System.out.println(result);
// (4+3)*2
result = evaluator.evaluate("4 3 + 2 *");
System.out.println(result);
}
测试结果:
3.0
5.0
14.0
解释器模式总结
解释器模式为自定义语言的设计和实现提供了一种解决方案,它用于定义一组文法规则并通过这组文法规则来解释语言中的句子。虽然解释器模式的使用频率不是特别高,但是它在正则表达式、XML文档解释等领域还是得到了广泛使用。
主要优点
- 易于改变和扩展文法。由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法。
- 每一条文法规则都可以表示为一个类,因此可以方便地实现一个简单的语言。
- 实现文法较为容易。在抽象语法树中每一个表达式节点类的实现方式都是相似的,这些类的代码编写都不会特别复杂,还可以通过一些工具自动生成节点类代码。
- 增加新的解释表达式较为方便。如果用户需要增加新的解释表达式只需要对应增加一个新的终结符表达式或非终结符表达式类,原有表达式类代码无须修改,符合 “开闭原则”。
主要缺点
- 对于复杂文法难以维护。在解释器模式中,每一条规则至少需要定义一个类,因此如果一个语言包含太多文法规则,类的个数将会急剧增加,导致系统难以管理和维护,此时可以考虑使用语法分析程序等方式来取代解释器模式。
- 执行效率较低。由于在解释器模式中使用了大量的循环和递归调用,因此在解释较为复杂的句子时其速度很慢,而且代码的调试过程也比较麻烦。
适用场景
- 可以将一个需要解释执行的语言中的句子表示为一个抽象语法树。
- 一些重复出现的问题可以用一种简单的语言来进行表达。
- 一个语言的文法较为简单。
- 对执行效率要求不高。
解释器模式的典型应用
Spring EL表达式中的解释器模式
Spring EL表达式相关的类在org.springframework.expression
包下,这里只是做简单的测试,需要进一步了解,请阅读源码。
/**
* 行为型模式——Spring EL表达式
*/
@Test
public void testBehaviorInterpreter1() {
// 1. 构建解析器
ExpressionParser parser = new SpelExpressionParser();
// 2. 解析表达式
Expression expression = parser.parseExpression("100 * 2 + 400 * 1 + 66");
// 3. 获取结果
int result = (Integer) expression.getValue();
// 结果:666
System.out.println(result);
}
结果:
666