介绍
解释器(Interpreter)模式的定义:
一种行为型设计模式。定义了一个解释器,来解释给定语言和文法的句子。其实质是把语言中的每个符号定义成一个(对象)类,从而把每个程序转换成一个具体的对象树。
案例
角色构成:
- AbstractExpression(抽象表达式):在抽象表达式中声明了抽象的解释操作,它是所有终结符表达式和非终结符表达式的公共父类。
- TerminalExpression(终结符表达式):终结符表达式是抽象表达式的子类,它实现了与文法中的终结符相关联的解释操作,在句子中的每一个终结符都是该类的一个实例。通常在一个解释器模式中只有少数几个终结符表达式类,它们的实例可以通过非终结符表达式组成较为复杂的句子。
- NonterminalExpression(非终结符表达式):非终结符表达式也是抽象表达式的子类,它实现了文法中非终结符的解释操作,由于在非终结符表达式中可以包含终结符表达式,也可以继续包含非终结符表达式,因此其解释操作一般通过递归的方式来完成。
- Context(环境类):环境类又称为上下文类,它用于存储解释器之外的一些全局信息,通常它临时存储了需要解释的语句。
这里我们编写一个案例:张三公司最近需要开发一款简单的加法/减法解释器,只要输入一个加法/减法表达式,它就能够计算出表达式结果,当输入字符串表达式为“1+2+3+4-5”时,将输出计算结果为3。
抽象表达式类
// 抽象表达式类
public abstract class AbstractExpression {
// 提供统一的解释接口
public abstract int interpret();
}
终结符类
// 终结符类
public class ValueExpression extends AbstractExpression {
private int value;
public ValueExpression(int value) {
this.value = value;
}
@Override
public int interpret() {
return value;
}
}
非终结符加法类
public class AddExpression extends AbstractExpression {
private AbstractExpression left;
private AbstractExpression right;
public AddExpression(AbstractExpression left, AbstractExpression right) {
this.left = left;
this.right = right;
}
@Override
public int interpret() {
return left.interpret() + right.interpret();
}
}
非终结符减法类
public class SubtractionExpression extends AbstractExpression {
private AbstractExpression left;
private AbstractExpression right;
public SubtractionExpression(AbstractExpression left, AbstractExpression right) {
this.left = left;
this.right = right;
}
@Override
public int interpret() {
return left.interpret() - right.interpret();
}
}
上下文计算器类
public class Calculator {
private AbstractExpression expression;
public void parse(String expression) {
String[] expressionArray = expression.split("");
Stack<AbstractExpression> stack = new Stack<>();
for (int i = 0; i < expressionArray.length; i++) {
if ("+".equals(expressionArray[i])) {
AbstractExpression left = stack.pop();
AbstractExpression right = new ValueExpression(Integer.parseInt(expressionArray[++i]));
stack.push(new AddExpression(left, right));
} else if ("-".equals(expressionArray[i])) {
AbstractExpression left = stack.pop();
AbstractExpression right = new ValueExpression(Integer.parseInt(expressionArray[++i]));
stack.push(new SubtractionExpression(left, right));
} else {
stack.push(new ValueExpression(Integer.parseInt(expressionArray[i])));
}
}
this.expression = stack.pop();
}
public int calculate() {
return expression.interpret();
}
}
客户端类
public class Client {
public static void main(String[] args) {
Calculator calculator = new Calculator();
String expression = "1+2+3+4-5";
calculator.parse(expression);
System.out.println(expression + "=" + calculator.calculate());
}
}
运行结果
1+2+3+4-5=5
优缺点及应用场景
优点:
- 扩展性好。由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法。
- 容易实现。在语法树中的每个表达式节点类都是相似的,所以实现其文法较为容易。
缺点:
- 执行效率较低。解释器模式中通常使用大量的循环和递归调用,当要解释的句子较复杂时,其运行速度很慢,且代码的调试过程也比较麻烦。
- 会引起类膨胀。解释器模式中的每条规则至少需要定义一个类,当包含的文法规则很多时,类的个数将急剧增加,导致系统难以管理与维护。
- 可应用的场景比较少。在软件开发中,需要定义语言文法的应用实例非常少,所以这种模式很少被使用到。
应用场景:
- 当语言的文法较为简单,且执行效率不是关键问题时。
- 当问题重复出现,且可以用一种简单的语言来进行表达时。
- 当一个语言需要解释执行,并且语言中的句子可以表示为一个抽象语法树的时候,如 XML 文档解释。
注意:解释器模式在实际的软件开发中使用比较少,因为它会引起效率、性能以及维护等问题。如果碰到对表达式的解释,在 Java 中可以用 Expression4J 或 Jep 等来设计。