解释器设计模式
1. 简单介绍
解释器设计模式(interpreter)是一种行为型设计模式。给定一个语言,定义他的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
2. 使用场景
- 可以将一个需要解释执行的语言中的句子表示为一个抽象语法树
- 一些重复出现的问题可以用一种简单的语言来进行表达
- 文法较为简单
3. 场景举例
编译器编辑的过程大致可以分为 词法分析、语法分析、语义分析、中间代码优化以及最终的最终代码生成。而解释器起始就是完成了语法树的解析,将一个个词组解释成一个个语法规范,交给下一个过程使用。 无论高级语言程序是怎么样编写的,编译器的代码是不用修改的,而解释器模式就是想做一个建立在Java
和我们自定义语言之间的编译器。
4. UML类图
5. 具体实现
描述
- 背景:给定一个包含加减的表达式如:a+b+c,分别给出
a
、b
、c
的值,自动计算出表达式的值 AbstractExpression
:抽象表达式,对应AbstractExpression角色。抽象语法树节点的抽象,每个节点有自己的解释操作interpret()VarExpression
:变量表达式:对应TerminalExpression角色,操作数的封装对象MathSymbolExpression
:操作符的父类,对应NonTerminalExpressionPlusExpression
:加法表达式:NonTerminalExpression的具体子类,将表达式左和右操作数进行+操作MinusExpression
:减法表达式:NonTerminalExpression的具体子类,将表达式左操作数减去右操作数Calculator
:将输入后的表达式每个遍历,并通过对应的Expression进行解释,提供calculate()接口,获取最终Expression的interpret()结果Client
:客户端,给定待计算表达式、各个变量的值
实现代码
AbstractExpression.java
/**
* 抽象表达式,对应AbstractExpression角色
* 抽象语法树节点的抽象,每个节点有自己的解释操作interpret()
*/
public abstract class AbstractExpression {
public abstract int interpret(Map<String, Integer> varMap);
}
VarExpression.java
/**
* 变量表达式:对应TerminalExpression
* 操作数的封装对象
*/
public class VarExpression extends AbstractExpression {
private String key;
public VarExpression(String key) {
this.key = key;
}
@Override
public int interpret(Map<String, Integer> varMap) {
Integer var = varMap.get(key);
return var == null ? 0 : var;
}
}
MathSymbolExpression .java
/**
* 操作符的父类,对应NonTerminalExpression
*/
public class MathSymbolExpression extends AbstractExpression {
/**
* 左操作数表达式
*/
protected AbstractExpression leftExpression;
/**
* 右操作数表达式
*/
protected AbstractExpression rightExpression;
public MathSymbolExpression(AbstractExpression leftExpression, AbstractExpression rightExpression) {
this.leftExpression = leftExpression;
this.rightExpression = rightExpression;
}
/**
* 空实现,具体的实现,交给具体的符号表达式实现
* @param varMap 变量Map
* @return 表达式计算后的结果
*/
@Override
public int interpret(Map<String, Integer> varMap) {
return 0;
}
}
PlusExpression.java
/**
* 加法表达式:NonTerminalExpression的具体子类
* 将表达式左和右操作数进行+操作
*/
public class PlusExpression extends MathSymbolExpression {
public PlusExpression(AbstractExpression leftExpression, AbstractExpression rightExpression) {
super(leftExpression, rightExpression);
}
@Override
public int interpret(Map<String, Integer> var) {
return super.leftExpression.interpret(var) + super.rightExpression.interpret(var);
}
}
MinusExpression.java
/**
* 减法表达式:NonTerminalExpression的具体子类
* 将表达式左操作数减去右操作数
*/
public class MinusExpression extends MathSymbolExpression {
public MinusExpression(AbstractExpression leftExpression, AbstractExpression rightExpression) {
super(leftExpression, rightExpression);
}
@Override
public int interpret(Map<String, Integer> varMap) {
return super.leftExpression.interpret(varMap) - super.rightExpression.interpret(varMap);
}
}
Calculator.java
/**
* 将输入后的表达式每个遍历,并通过对应的Expression进行解释
* 提供calculate()接口,获取最终Expression的interpret()结果
*/
public class Calculator {
private String expStr;
/**
* 定义表达式
*/
private AbstractExpression expression;
public Calculator(String expStr) {
this.expStr = expStr;
}
public void analyse() {
Stack<AbstractExpression> expStack = new Stack<>();
AbstractExpression left;
AbstractExpression right;
char[] expChar = expStr.toCharArray();
for (int i = 0; i < expChar.length; i++) {
char curExpChar = expChar[i];
switch (curExpChar) {
case '+':
left = expStack.pop();
right = new VarExpression(String.valueOf(expChar[++i]));
PlusExpression sumExpression = new PlusExpression(left, right);
expStack.push(sumExpression);
break;
case '-':
left = expStack.pop();
right = new VarExpression(String.valueOf(expChar[++i]));
MinusExpression diffExpression = new MinusExpression(left, right);
expStack.push(diffExpression);
break;
default:
expStack.push(new VarExpression(String.valueOf(curExpChar)));
break;
}
}
this.expression = expStack.pop();
}
public int calculate(Map<String, Integer> varMap) {
return this.expression.interpret(varMap);
}
}
Client.java
/**
* 客户端
* 给定待计算表达式、各个变量的值
*/
public class Client {
public static void main(String[] args) {
// 1. 给定待计算表达式文法
String expStr = "a+b+c-d+e";
// 2. 给待计算表达式文法变量输入值
HashMap<String, Integer> varMap = new HashMap<>();
varMap.put("a", 1);
varMap.put("b", 2);
varMap.put("c", 3);
varMap.put("d", 4);
varMap.put("e", 5);
// 3. 文法解析
Calculator calculator = new Calculator(expStr);
calculator.analyse();
// 4. 计算并打印
System.out.println(calculator.calculate(varMap));
}
}
7. 源码展示
Spring中的对象求值表达式Expression
的SpelExpression
封装了解析算术表达式字符串的方法,该解析方法的实现采用了解释器模式。其中解释器模式中抽象表达式角色对应Spring
中的Expression
接口类,该类中的getValue()
方法对应解释器模式中的interpret()
方法。具体的Expression
实现类有SpelExpression
、LiteralExpression
和CompositeStringExpression
。
Expression.java
/**
* 抽象表达式,对应AbstractExpression角色
* 抽象语法树节点的抽象,每个节点有自己的解释操作interpret()
*/
public interface Expression {
/**
* evaluation result 解析表达的值
*/
@Nullable
Object getValue() throws EvaluationException;
}
SpelExpression.java
public class SpelExpression implements Expression {
/**
* 对Spel这种特定的表达式进行解释计算
*/
@Override
@Nullable
public Object getValue() throws EvaluationException {
if (this.compiledAst != null) {
try {
EvaluationContext context = getEvaluationContext();
return this.compiledAst.getValue(context.getRootObject().getValue(), context);
}
catch (Throwable ex) {
// If running in mixed mode, revert to interpreted
if (this.configuration.getCompilerMode() == SpelCompilerMode.MIXED) {
this.interpretedCount = 0;
this.compiledAst = null;
}
else {
// Running in SpelCompilerMode.immediate mode - propagate exception to caller
throw new SpelEvaluationException(ex, SpelMessage.EXCEPTION_RUNNING_COMPILED_EXPRESSION);
}
}
}
ExpressionState expressionState = new ExpressionState(getEvaluationContext(), this.configuration);
Object result = this.ast.getValue(expressionState);
checkCompile(expressionState);
return result;
}
}