介绍
解释器(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(); } }
客户端类
运行结果
1+2+3+4-5=5
优缺点及应用场景
优点:
- 扩展性好。由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法。
- 容易实现。在语法树中的每个表达式节点类都是相似的,所以实现其文法较为容易。
缺点:
- 执行效率较低。解释器模式中通常使用大量的循环和递归调用,当要解释的句子较复杂时,其运行速度很慢,且代码的调试过程也比较麻烦。
- 会引起类膨胀。解释器模式中的每条规则至少需要定义一个类,当包含的文法规则很多时,类的个数将急剧增加,导致系统难以管理与维护。
- 可应用的场景比较少。在软件开发中,需要定义语言文法的应用实例非常少,所以这种模式很少被使用到。
应用场景:
- 当语言的文法较为简单,且执行效率不是关键问题时。
- 当问题重复出现,且可以用一种简单的语言来进行表达时。
- 当一个语言需要解释执行,并且语言中的句子可以表示为一个抽象语法树的时候,如 XML 文档解释。
注意:解释器模式在实际的软件开发中使用比较少,因为它会引起效率、性能以及维护等问题。如果碰到对表达式的解释,在 Java 中可以用 Expression4J 或 Jep 等来设计。