一、概述
解释器模式(Interpreter Pattern)实现了一个表达式接口,该接口解释一个特定的上下文。这种模式被用在 SQL 解析、符号处理引擎等。
给定一个语言(汉语),定义它的文法(主谓宾)表示,并定义一个解释器,使用该解释器来解释语言中的句子。
主要解决:
对于一些固定文法构建一个解释句子的解释器。
何时使用:
如果一种特定类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子。这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题。
如何解决:
构建语法树,定义终结符与非终结符。
关键代码:
构建环境类,包含解释器之外的一些全局信息,一般是 HashMap。
应用实例:
编译器、运算表达式计算。
优点:
1、可扩展性比较好,灵活。
2、增加了新的解释表达式的方式。
3、易于实现简单文法。
缺点:
1、可利用场景比较少。
2、对于复杂的文法比较难维护。
3、解释器模式会引起类膨胀。
4、解释器模式采用递归调用方法。
使用场景:
1、可以将一个需要解释执行的语言中的句子表示为一个抽象语法树。
2、一些重复出现的问题可以用一种简单的语言来进行表达。
3、一个简单语法需要解释的场景。
注意事项:
可利用场景比较少,JAVA 中如果碰到可以用 expression4J 代替。
Context:环境角色(上下文),包含解释器之外的一些全局信息。
AbstractExpression:抽象表达式类,声明了抽象的解释操作,所有解释器类都继承或实现该类。这个接口为抽象语法树中所有的节点所共享
TerminalExpression:终结符表达式类,是AbstractExpression的子类,实现了文法中有关终结符相关的解释操作。 一个句子中的每个终结符需要该类的一个实例
NonTerminalExpression:非终结符表达式类,AbstractExpression的子类,该类的功能与终结表达式类相反,为文法中的非终结符实现解释操作
Client:客户端测试类。
二、代码测试
/**
* 抽象解释器
*/
public abstract class AbstractExpression {
/**
* 解释表达式的抽象方法
*
* @param map 比如现有的表达式a+b, 那么map中应该存放的是{a=10,b=20}
* @return
*/
public abstract int interpret(Map<String, Integer> map);
}
/**
* 运算符-解析器类,非终结
*/
public class SymbolExpression extends AbstractExpression {
/**
* 假如现有表达式 : a + b - c 需要解析
* 分析:
* 1、一个运算符连接的是它左右两个数字
* 2、如上表达式 + 号连接的是 a 和 b,- 号连接的是 a + b 和 c
* 3、所以我们将运算符的左右都看成是一个表达式也就是Expression
*/
protected AbstractExpression leftExpression;
protected AbstractExpression rightExpression;
public SymbolExpression(AbstractExpression leftExpression, AbstractExpression rightExpression) {
this.leftExpression = leftExpression;
this.rightExpression = rightExpression;
}
/**
* 不同种类的运算符由不同的运算符子类进行解析,所以该类不实现interpreter方法
*
* @param map 比如现有的表达式a+b, 那么map中应该存放的是{a=10,b=20}
* @return
*/
@Override
public int interpret(Map<String, Integer> map) {
return 0;
}
}
/**
* 非终结符解释器-add
*/
public class AddExpression extends SymbolExpression {
public AddExpression(AbstractExpression leftExpression, AbstractExpression rightExpression) {
super(leftExpression, rightExpression);
}
@Override
public int interpret(Map<String, Integer> map) {
return leftExpression.interpret(map) + rightExpression.interpret(map);
}
}
/**
* 非终结符解释器-sub
*/
public class SubExpression extends SymbolExpression {
public SubExpression(AbstractExpression leftExpression, AbstractExpression rightExpression) {
super(leftExpression, rightExpression);
}
@Override
public int interpret(Map<String, Integer> map) {
return leftExpression.interpret(map) - rightExpression.interpret(map);
}
}
/**
* 终结符解释器
*/
public class VarExpression extends AbstractExpression {
// 公式中的变量
private String key;
public VarExpression(String key) {
this.key = key;
}
// 通过key获取对应的值
@Override
public int interpret(Map<String, Integer> map) {
return map.get(key);
}
}
/**
* 计算器
*/
public class CalculatorContext {
private AbstractExpression expression;
public CalculatorContext(String expressionString) {
init(expressionString);
}
/**
* 表达式解析
*
* @param expressionString 语句
*/
private void init(String expressionString) {
List<Character> charArray = infix2suffix(expressionString);
// [a,d,+,c,-]
System.out.println(charArray);
Stack<AbstractExpression> stack = new Stack<>();
for (char item : charArray) {
if (item == '+') {
if (stack.size() < 2) {
throw iae();
}
AbstractExpression right = stack.pop();
AbstractExpression left = stack.pop();
// stack push数值-AddExpression
stack.push(new AddExpression(left, right));
} else if (item == '-') {
if (stack.size() < 2) {
throw iae();
}
AbstractExpression right = stack.pop();
AbstractExpression left = stack.pop();
// stack push数值-SubExpression
stack.push(new SubExpression(left, right));
} else if (item <= 'z' && item >= 'a') {
// stack push数值-VarExpression
stack.push(new VarExpression(String.valueOf(item)));
} else {
throw iae();
}
}
if (stack.size() != 1) {
throw iae();
}
this.expression = stack.pop();
}
/**
* 中缀表达式转后缀表达式
*/
private List<Character> infix2suffix(String infix) {
// 字母集合
List<Character> res = new ArrayList<>();
// 表达式栈
Stack<Character> stack = new Stack<>();
// 基础操作符集合
Set<Character> operatorSet = new HashSet<>();
operatorSet.add('+');
operatorSet.add('-');
for (char item : infix.toCharArray()) {
//是字母,直接添加到结果列表
if (item <= 'z' && item >= 'a') {
res.add(item);
continue;
}
// 操作符
if (operatorSet.contains(item)) {
if (!stack.isEmpty()) {
// 弹出操作符,加入到res
res.add(stack.pop());
}
// stack压入操作符
stack.push(item);
} else {
throw iae();
}
}
while (!stack.isEmpty()) {
res.add(stack.pop());
}
return res;
}
private IllegalArgumentException iae() {
return new IllegalArgumentException("illegal expression");
}
public int run(Map<String, Integer> var) {
return this.expression.interpret(var);
}
}
/**
* 客户端
*/
public class Client {
public static void main(String[] args) throws IOException {
String expStr = "a+b-c";
Map<String, Integer> context = new HashMap<>();
context.put("a", 12);
context.put("b", 34);
context.put("c", 56);
// 上下文,并解析
CalculatorContext calculator = new CalculatorContext(expStr);
System.out.println("运算结果:" + expStr + "=" + calculator.run(context));
}
}
三、解释器模式在Spring中的实现
Spring中的SpelExpressionParser
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
public class Client {
public static void main(String[] args) {
SpelExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression("#a+#b-#c");
EvaluationContext context = new StandardEvaluationContext();
context.setVariable("a",12);
context.setVariable("b",34);
context.setVariable("c",56);
System.out.println(expression.getValue(context));//-10
}
}
表达式解析器SpelExpressionParser根据字符串解析成Expression对象,根据EvaluationContext上下文对象获取执行结果。
各种操作符如加号减号都是SpelNodeImpl对象。