解释器模式(Interpreter Pattern)是一种行为设计模式,它定义了一种语言,并使用该语言来解释句子。这种模式用于描述如何构成一个简单的语言解析器。在Java中实现解释器模式通常涉及一个抽象表达式接口、具体表达式类(终端表达式和非终端表达式)、以及上下文环境类。
- AbstractExpression 是所有表达式的抽象基类,声明了一个 interpret() 方法,用于解释操作。
- TerminalExpression 是最终表达式类,实现了具体的解释逻辑。
- NonTerminalExpression 是非终极表达式类,除了实现 interpret() 方法外,还可能包含组合其他表达式的方法(如 add() 或 setNext()),用于构建更复杂的表达式结构。
- Context 类包含了表达式解释所需的环境信息,供表达式在解释时使用。
实现四则运算
我们使用解释器模式实现了一个简单的算术表达式求值功能。Interpreter类的evaluate()方法接受一个字符串形式的表达式,然后通过栈和递归的方式解析并计算表达式的值。
- 首先,我们定义一个表达式接口Expression,它有一个解释方法interpret():
public interface Expression {int interpret(HashMap<String,Integer> map);
}
- 创建两个具体的表达式类:最终表达式NumberExpression和非终极表达式AddExpression,分别表示数字和加法操作。他们都从map中获取值所需要的值:
public class NumberExpression implements Expression {private String key;public NumberExpression(String key) {this.key = key;}@Overridepublic int interpret(HashMap<String,Integer> map) {return map.get(key);}
}private Expression leftExpression;private Expression rightExpression;public AddExpression(Expression leftExpression, Expression rightExpression) {this.leftExpression = leftExpression;this.rightExpression = rightExpression;}@Overridepublic int interpret(HashMap<String, Integer> map) {return leftExpression.interpret(map)+rightExpression.interpret(map);}
同样的写一个减法:
public class SubExpression implements Expression {private Expression leftExpression;private Expression rightExpression;public SubExpression(Expression leftExpression, Expression rightExpression) {this.leftExpression = leftExpression;this.rightExpression = rightExpression;}@Overridepublic int interpret(HashMap<String, Integer> map) {return leftExpression.interpret(map) - rightExpression.interpret(map);}
}
- 我们创建一个解释器类Interpreter,用于解释表达式。然后通过栈和递归的方式解析并计算表达式的值:
import java.util.Stack;public class Interpreter {public Expression expression;
/*** Interpreter类用于解析并执行给定的表达式。* * @param expression 表达式字符串,支持加减运算和整数。*/
public Interpreter(String expression) {Stack<Expression> stack = new Stack<>();char[] tokens = expression.toCharArray();for (int i = 0; i < tokens.length; i++) {if ('+' == tokens[i]) {// 当遇到加号时,从栈中弹出右操作数,创建一个加法表达式并压入栈中Expression rightExpression = stack.pop();Expression leftExpression = new NumberExpression(String.valueOf(tokens[++i]));stack.push(new AddExpression(leftExpression, rightExpression));} else if ('-' == tokens[i]) {// 当遇到减号时,从栈中弹出右操作数,创建一个减法表达式并压入栈中Expression rightExpression = stack.pop();Expression leftExpression = new NumberExpression(String.valueOf(tokens[++i]));stack.push(new SubExpression(leftExpression, rightExpression));} else {// 遇到其他字符(假设为数字)时,创建一个数字表达式并压入栈中stack.push(new NumberExpression(String.valueOf(tokens[i])));}}this.expression = stack.pop(); // 最后栈中剩下的就是解析后的表达式
}/*** 执行解析后的表达式,并返回结果。* * @param map 用于解析表达式中变量的值的映射。* @return 表达式的计算结果。*/
public int run(HashMap<String, Integer> map) {return this.expression.interpret(map);
}
}
- 使用:
@Testvoid contextLoads() {HashMap<String, Integer> map = new HashMap<>();map.put("a", 10);map.put("b", 20);map.put("c", 30);String expression = "a+b-c";Interpreter in = new Interpreter(expression);int run = in.run(map);System.out.println(run);//0}
解释器模式 优缺点
解释器模式的优点主要包括:
- 扩展性好:由于使用类来表示语言的文法规则,可以通过继承等机制来改变或扩展文法。
- 易于实现简单文法:在语法树中的每个表达式节点类都是相似的,所以实现其文法较为容易。
解释器模式的缺点包括:
- 执行效率较低:通常使用大量的循环和递归调用,当要解释的句子较复杂时,运行速度很慢,且代码的调试过程也比较麻烦。
- 引起类膨胀:每条规则至少需要定义一个类,当包含的文法规则很多时,类的个数将急剧增加,导致系统难以管理与维护。
- 应用场景少:在软件开发中,需要定义语言文法的应用实例非常少,所以这种模式很少被使用到。
典型应用
Expression4J是一个Java库,它允许你解析和执行数学表达式。这个库是设计模式应用的一个很好的例子,因为它使用解释器模式来处理和计算表达式:
- 文法表示:Expression4J定义了一套规则来描述数学表达式的结构和语法。
- 解释器构建:该库包含一个解释器,它能够理解这些规则并将输入的表达式转换为可执行的代码。
- 上下文环境:在解释器模式中,通常会有一个环境类来存储全局信息,这可能是以HashMap的形式实现的。虽然Expression4J的具体实现细节可能不同,但概念上它提供了一种方式来管理表达式求值所需的上下文信息。
- 适用场景:解释器模式适用于那些需要频繁解释特定类型语言的问题。由于Expression4J是为了解决数学表达式求值这一问题而设计的,它正好符合解释器模式的应用场景。