基本概念
解释器模式 Interpreter Pattern 属于行为模式,根据给定的语言表达式,选择定义的解释器,使用不同的解释器来解析表达式得到最终的结果。类似的设计模式在SQL解析,正则表达式,计算器,编译器,模板引擎等大量使用。
UML
角色分析
Context 环境上下文 保存除解释器外的全局信息
AbstractExpression 抽象表达式 声明抽象解释操作 为语法树中所有节点所共享
TerminalExpreesion 终结符表达式 文法中终结相关的解释操作 类似计算中的 等于 =
NonTerminalExpression 非终结符操作 文法中非终结相关操作 类似计算中 + - * /
Client 客户端调用 使用Context 和AbstractExpression的实现类
代码实现
通过解释器模式来实现四则运算, 如计算 a+b-c 的值
// AbstractExpression 抽象表达式 这里以返回整数加减为例
public interface Expression {
// 最终整数加减返回还是整数 var 是保存了变量和变量的值
// 如 a+b+c var 中保存的就是 a->1 b->2 c->3 相当于把表达式中的值保存起来
// 如果直接是1+2+3 也可以直接用List保存
int interpreter(HashMap<String, Integer> var);
}
// 变量值expression 相当于从Map中获取具体的一个数值 供操作expression调用
public class VarExpression implements Expression {
private String key; // key=a,key=b,key=c
public VarExpression(String key) {
this.key = key;
}
@Override
public int interpreter(HashMap<String, Integer> var) {
return var.get(key);
}
}
// 中间Expression 核心在于 定义了Expression的解析结构 这里加减法需要
// 加数和被加数 就定义成了left 和 right left相当于就加数 right为被加数
public class SymbolExpression implements Expression {
protected Expression left;
protected Expression right;
public SymbolExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}
@Override
public int interpreter(HashMap<String, Integer> var) {
// 空实现
return 0;
}
}
// 加减法具体的操作
public class AddExpression extends SymbolExpression {
public AddExpression(Expression left, Expression right) {
super(left, right);
}
@Override
public int interpreter(HashMap<String, Integer> var) {
// 本质上就是调用left的interpreter方法获取加数 right获取被加数
// left里面如果也是SymbolExpression会进一步递归进去
// 最终会找到VarExpression VarExpression从Map中找到具体的数值返回
// 然后加法就用 + 操作 减法 - 操作
return left.interpreter(var) + right.interpreter(var);
}
}
public class SubExpression extends SymbolExpression {
public SubExpression(Expression left, Expression right) {
super(left, right);
}
@Override
public int interpreter(HashMap<String, Integer> var) {
return left.interpreter(var) - right.interpreter(var);
}
}
// 最核心的操作类
public class Calculator {
// 最终的表达式
public Expression expression;
public Calculator(String exp) {
// 栈 结构保存数据 先进先出
Deque<Expression> deque = new LinkedBlockingDeque<>();
String[] strs = exp.split("");
// 将表达式切分出来 a+b+c 切成 [a,b,c]集合
List<String> expEle = Arrays.stream(strs).filter(str -> !"".equals(str.trim())).collect(Collectors.toList());
// 用于声明left right
Expression left = null;
Expression right = null;
for (int i = 0; i < expEle.size(); i++) {
String ele = expEle.get(i);
switch (ele) {
case "+":
// 弹出左边的left Expression
left = deque.pop();
// 因为是+号 他的right 的VarExpression 就是 ++i
//相当于渠道+号之后的元素 然后++i 让索引跳过+号
right = new VarExpression(expEle.get(++i));
deque.push(new AddExpression(left, right));
break;
case "-":
left = deque.pop();
right = new VarExpression(expEle.get(++i));
deque.push(new SubExpression(left, right));
break;
default:
// 如果不是操作符就直接构建一个varExpression
deque.push(new VarExpression(ele));
}
}
// 最终被递归包装了很多层的表达式
this.expression = deque.pop();
}
public int run(HashMap<String, Integer> var) {
return expression.interpreter(var);
}
}
// 客户端调用
public class ExpressionTest {
public static void main(String[] args) {
String exp = "a+b+c";
HashMap<String, Integer> map = ExpressionTest.getMap(exp);
Calculator calculator = new Calculator(exp);
int result = calculator.run(map);
System.out.println(result);
}
// 相当于给a+b+c 分别给abc复制
private static HashMap<String, Integer> getMap(String exp) {
char[] chars = exp.toCharArray();
HashMap<String, Integer> params = new HashMap<>();
Scanner scanner = new Scanner(System.in);
for (char c : chars) {
if (c != '+' && c != '-') {
if (!params.containsKey(String.valueOf(c))) {
System.out.print("请输入" + String.valueOf(c) + "的值:");
int val = scanner.nextInt();
params.put(String.valueOf(c), val);
}
}
}
scanner.close();
return params;
}
}
使用细节
- 将一种特定的语法抽象成语法树,就考虑使用解释器模式,让程序扩展性更好,比如添加了一个新的语法如以上列子添加一个乘法操作只需要再新增一个MultipleExpression,并在Calcator中添加一个Switch条件即可增加乘法功能
- 一般用在需要解析特定约定好的语法的时候使用
- 解释器会导引类膨胀,因为解释器中大量使用了递归,在调式的时候会很复杂,使用的场景比较有限