第四篇 生成对象树 第十四章 - 解释器模式
对象树不仅可以表示对象在结构上的组成关系,还可以表示对象在逻辑上的运算关系。
下面以四则运算代数式的求值过程为例,考察对象树的作用。
给出任意四则运算代数式,比如
a * (b + c) + d (式一)
简便起见,先把它转换成逆波兰表达式(Reverse Polish Notation,RPN)。逆波兰表达式可以帮我们解决算符的优先级问题(() >* >+)。(式一)转换后变成
a b c + * d + (式二)
可以使用下面的简易工具进行转换。
// 代数式工具
public class ExpressionUtil {
// 算符列表
private static List<String> OPS = Arrays.asList("+", "-", "*", "/", "(", ")");
// 将四则运算代数式转换成逆波兰表达式
public static List<String> convertToRPN(String expression) {
List<String> expArr = split(expression);
List<String> result = new ArrayList<>();
Stack<Operator> s = new Stack<>();
for (String exp : expArr) {
if (!OPS.contains(exp)) {
result.add(exp);
continue;
}
switch (exp) {
case "+":
case "-":
while (!s.isEmpty() && s.peek().level >= 1) {
result.add(s.pop().op);
}
s.push(new Operator(exp, 1));
break;
case "*":
case "/":
while (!s.isEmpty() && s.peek().level >= 2) {
result.add(s.pop().op);
}
s.push(new Operator(exp, 2));
break;
case "(":
s.push(new Operator(exp, 0));
break;
case ")":
while (!s.isEmpty()) {
if ("(".equals(s.peek().op)) {
s.pop();
break;
} else {
result.add(s.pop().op);
}
}
break;
default:
throw new UnsupportedOperationException(exp);
}
}
while (!s.isEmpty()) {
result.add(s.pop().op);
}
return result;
}
// 分解代数式
public static List<String> split(String exp) {
String regx = "\\s+|\\" + String.join("|\\", OPS);
List<String> result = new ArrayList<>();
Pattern p = Pattern.compile(regx);
Matcher m = p.matcher(exp);
int start = 0;
while (m.find()) {
String s = exp.substring(start, m.start()).trim();
String g = m.group().trim();
if (s.length() > 0) result.add(s);
if (g.length() > 0) result.add(g);
start = m.end();
}
String l = exp.substring(start).trim();
if (l.length() > 0) result.add(l);
return result;
}
// “算符”类
static class Operator {
String op;// 符号
int level;// 优先级
public Operator(String op, int level) {
this.op = op;
this.level = level;
}
}
}
求值时先把(式二)转换成对象树,再遍历树中元素,代入具体数值后求出结果。这种计算方法也被称为解释器模式。
看下生成对象树的代码。
// 表达式
public abstract class Expression {
// 求值
public abstract Double interpret(Map<String, Double> vars);
}
// 算术表达式
public abstract class ArithExpression extends Expression {
protected Expression a;
protected Expression b;
protected ArithExpression(Expression a, Expression b) {
this.a = a;
this.b = b;
}
}
// 加
public class AddExpression extends ArithExpression {
public AddExpression(Expression a, Expression b) {
super(a, b);
}
@Override
public Double interpret(java.util.Map<String, Double> vars) {
return b.interpret(vars) + a.interpret(vars);
}
}
// 减
public class SubExpression extends ArithExpression {
public SubExpression(Expression a, Expression b) {
super(a, b);
}
@Override
public Double interpret(java.util.Map<String, Double> vars) {
return b.interpret(vars) - a.interpret(vars);
}
}
// 乘
public class MulExpression extends ArithExpression {
public MulExpression(Expression a, Expression b) {
super(a, b);
}
@Override
public Double interpret(java.util.Map<String, Double> vars) {
return b.interpret(vars) * a.interpret(vars);
}
}
// 除
public class DivExpression extends ArithExpression {
public DivExpression(Expression a, Expression b) {
super(a, b);
}
@Override
public Double interpret(java.util.Map<String, Double> vars) {
return b.interpret(vars) / a.interpret(vars);
}
}
// 数值表达式
public class VarExpression extends Expression {
private String key;
public VarExpression(String key) {
this.key = key;
}
@Override
public Double interpret(java.util.Map<String, Double> vars) {
return vars.get(this.key);
}
}
// 对象树工具
public class ExpressionTreeUtil {
// 生成对象树
public static Expression create(java.util.List<String> rpn) {
java.util.Stack<Expression> s = new java.util.Stack<>();
for (String exp : rpn) {
switch (exp) {
case "+":
s.push(new AddExpression(s.pop(), s.pop()));
break;
case "-":
s.push(new SubExpression(s.pop(), s.pop()));
break;
case "*":
s.push(new MulExpression(s.pop(), s.pop()));
break;
case "/":
s.push(new DivExpression(s.pop(), s.pop()));
break;
default:
s.push(new VarExpression(exp));
}
}
return s.pop();
}
}
此例生成的对象树如下图所示。

从图中可以看出,末端元素负责提供数值,复合元素负责计算。计算任务将从根元素开始,至遍历完整棵对象树结束。
使用效果如下。
import java.util.*;
// 测试类
public class Test {
public void test() {
// 输入四则运算代数式
String exp = "a * (b + c) + d";
// 将四则运算代数式转换成逆波兰表达式
List<String> rpn = ExpressionUtil.convertToRPN(exp);
// 生成对象树
Expression tree = ExpressionTreeUtil.create(rpn);
// 代入具体数值
Map<String, Double> vars = new HashMap<>();
vars.put("a", 2.0);
vars.put("b", 4.0);
vars.put("c", 6.0);
vars.put("d", 8.0);
// 计算结果
Double result = tree.interpret(vars);
}
}
浙公网安备 33010602011771号