对解释器模式的理解
- 一、场景
- 1、题目【[来源](https://kamacoder.com/problempage.php?pid=1096)】
- 1.1 题目描述
- 1.2 输入描述
- 1.3 输出描述
- 1.4 输入示例
- 1.5 输出示例
- 二、不采用解释器模式
- 1、代码
- 2、“缺点”
- 三、采用解释器模式
- 1、代码
- 2、“优点”
- 四、思考
- 1、解释器模式的意义
一、场景
1、题目【来源】
1.1 题目描述
小明正在设计一个计算器,用于解释用户输入的简单数学表达式,每个表达式都是由整数、加法操作符+、乘法操作符组成的,表达式中的元素之间用空格分隔,请你使用解释器模式帮他实现这个系统。
1.2 输入描述
每行包含一个数学表达式,表达式中包含整数、加法操作符(+)和乘法操作符(*)。 表达式中的元素之间用空格分隔。
1.3 输出描述
对于每个输入的数学表达式,每行输出一个整数,表示对应表达式的计算结果。
1.4 输入示例
2 + 3
5 * 2
3 + 4 * 2
1.5 输出示例
5
10
11
二、不采用解释器模式
1、代码
- 计算器
public class Calculator {
private final Deque<Integer> numberStack;
private final Deque<String> operatorStack;
public Calculator() {
numberStack = new ArrayDeque<>();
operatorStack = new ArrayDeque<>();
}
public Integer calculate(String expression) {
if (expression == null || expression.isEmpty()) {
return null;
}
String[] tokens = expression.trim().split("\\s+");
for (String token : tokens) {
if (isOperator(token)) {
while (!operatorStack.isEmpty() && ("*".equals(operatorStack.peek()) && "+".equals(token))) {
numberStack.push(numberStack.pop() * numberStack.pop());
}
operatorStack.push(token);
} else {
numberStack.push(Integer.parseInt(token));
}
}
while (!operatorStack.isEmpty()) {
Integer num2 = numberStack.pop();
Integer num1 = numberStack.pop();
String operator = operatorStack.pop();
if ("+".equals(operator)) {
numberStack.push(num1 + num2);
} else if ("*".equals(operator)) {
numberStack.push(num1 * num2);
}
}
return numberStack.pop();
}
private static boolean isOperator(String s) {
return "+".equals(s) || "*".equals(s);
}
}
- 客户端
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
Calculator calculator = new Calculator();
while (scanner.hasNextLine()) {
String expression = scanner.nextLine();
System.out.println(calculator.calculate(expression));
}
}
}
2、“缺点”
- 倒反天罡了,我觉得不采用解释器模式反而更好。😃
三、采用解释器模式
1、代码
- 定义表达式
public interface Expression {
int interpret();
}
public class NumberExpression implements Expression {
private final int number;
public NumberExpression(int number) {
this.number = number;
}
@Override
public int interpret() {
return number;
}
}
public class AddExpression implements Expression {
private final Expression left;
private final Expression right;
public AddExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}
@Override
public int interpret() {
return left.interpret() + right.interpret();
}
}
public class MultiplyExpression implements Expression {
private final Expression left;
private final Expression right;
public MultiplyExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}
@Override
public int interpret() {
return left.interpret() * right.interpret();
}
}
- 计算器
public class Calculator {
private final Deque<Expression> expressionStack = new ArrayDeque<>();
private final Deque<String> operatorStack = new ArrayDeque<>();
public Integer calculate(String expression) {
if (expression == null || expression.isEmpty()) {
return null;
}
String[] tokens = expression.trim().split("\\s+");
for (String token : tokens) {
if (isOperator(token)) {
// 处理操作符优先级
while (!operatorStack.isEmpty() && ("*".equals(operatorStack.peek()) && "+".equals(token))) {
processOperator();
}
operatorStack.push(token);
} else {
expressionStack.push(new NumberExpression(Integer.parseInt(token)));
}
}
// 处理剩余的操作符
while (!operatorStack.isEmpty()) {
processOperator();
}
// 最终栈中只剩下一个表达式对象
return expressionStack.pop().interpret();
}
private void processOperator() {
Expression right = expressionStack.pop();
Expression left = expressionStack.pop();
String operator = operatorStack.pop();
Expression operation;
if ("+".equals(operator)) {
operation = new AddExpression(left, right);
} else if ("*".equals(operator)) {
operation = new MultiplyExpression(left, right);
} else {
throw new IllegalArgumentException("Unknown operator: " + operator);
}
expressionStack.push(operation);
}
private static boolean isOperator(String s) {
return "+".equals(s) || "*".equals(s);
}
}
- 客户端代码和之前一样
2、“优点”
- 有种画蛇添足的感觉,貌似没有体现解释器模式的优势。
四、思考
1、解释器模式的意义
- 抽丝剥茧,从一段简单的代码说起:
public class InterpretTest {
public static void main(String[] args) {
// 3 + 4 * 2
Expression expression = new AddExpression(new NumberExpression(3),
new MultiplyExpression(new NumberExpression(4), new NumberExpression(2)));
System.out.println(expression.interpret());
}
}
-
当用户输入字符串“3 + 4 * 2”的时候,转换成Expression本身就比较复杂,但一旦转换好了,如上所示,Expression就发挥优势了。
-
因此,当我们需要解释语法规则时,应该是分为2步的:
- (1)将字符串类型的表达式,转成Expression对象。
- (2)Expression对象解释执行。【这一步才体现了解释模式的价值】
如果不采用解释模式,实际上如同“二、不采用解释器模式”的代码所示,是一边解析表达式字符串,一边执行的。
-
如果解释执行本身就比较简单(计算器的例子便是如此),先转成Expression对象再解释执行的意义确实不大。
-
但如果解释执行本身就比较复杂,而且如何解释执行可能变化,这时候解释器模式的价值就体现出来了。
目前没在实战中遇到过,等遇到了感受就深了。