解释器模式
一、定义
定于语言的文法,并且建立一个解释器来解释该语言的句子,这里的语言是指使用规定格式的语法和语法的代码,是一种类行为型模式
二、模式结构
1、类图

2、包含角色
2.1、AbstractExpression(抽象表达式)
在抽象表达式中声明了抽象的解释操作,它是所有的终结符表达式和非终结符表达式的共同父类
2.2、TerminalExpression(终结符表达式)
终结符表达式是抽象表达式的子类,它实现了与文法中的终结符相关联的解释操作,在句子中的每一个终结符都是该类的一个实例,通常在一个解释器模式中只有少数个终结符表达式,它们的实例可以通过非终结符表达式组成较为复杂的句子
2.3、NonterminalExpression(非终结符表达式)
非终结符表达式也是抽象表达式的子类,它实现了文法中非终结符的解释操作,由于在非终结符表达式中可以包含终结符表达式,一可以继续包含非终结符表达式,因此解释操作一般通过递归的方式来完成
2.4、Context(环境类)
又称为上下文类,用于存储解释器之外的一些全局信息,通常它临时存储了需要解释的语句
2.5、Client(客户类)
在客户类中构造了表示该文法定义的语言中一个特定句子的抽象语法树,该抽象语法树有非终结符表达式和终结符表达式实例组合而成,在客户类中还将调用解释操作,实现对句子的解释,有时候为了简化客户类的代码,可以将抽象语法树的构造工作封装到专门的类中完成,客户端只需要提供待解释的句子并调用该类的解释操作即可,该类可以称为解释器封装类
三、模式分析
解释器模式描述了如何为简单的语言定义一个文法,如何在该语言中表示一个句子,以及如何解释这些句子,在解释器模式中,每一种终结符和非终结符都有一个具体类与之对应,正因为使用类来表示每一个语法规则,使得系统具有较好的扩展性和灵活性。
四、解释器模式实例(表达式计算器)
1、类图

2、代码
2.1、抽象表示类
- Node
public interface Node {
int interpret();
}
2.2、终结符表达式类
- ValueNode
public class ValueNode implements Node{
private int value;
public ValueNode(int value) {
this.value = value;
}
@Override
public int interpret() {
return this.value;
}
}
2.3、抽象非终结符表达式类
- SymbolNode
public abstract class SymbolNode implements Node{
protected Node left;
protected Node right;
public SymbolNode(Node left, Node right) {
this.left = left;
this.right = right;
}
}
2.4、非终结符表达式类
- MulNode
public class MulNode extends SymbolNode{
public MulNode(Node left, Node right) {
super(left, right);
}
@Override
public int interpret() {
return super.left.interpret() * super.right.interpret();
}
}
- ModNode
public class ModNode extends SymbolNode{
public ModNode(Node left, Node right) {
super(left, right);
}
@Override
public int interpret() {
return super.left.interpret() % super.right.interpret();
}
}
- DivNode
public class DivNode extends SymbolNode{
public DivNode(Node left, Node right) {
super(left, right);
}
@Override
public int interpret() {
return super.left.interpret() / super.right.interpret();
}
}
2.5、解释器封装类
- Calculator
import java.util.Stack;
public class Calculator {
private String statement;
private Node node;
public void build(String statement) {
Node left = null, right = null;
Stack stack = new Stack();
String[] statementArr = statement.split(" ");
for (int i = 0; i < statementArr.length; i++) {
if (statementArr[i].equalsIgnoreCase("*")) {
left = (Node) stack.pop();
int val = Integer.parseInt(statementArr[++i]);
right = new ValueNode(val);
stack.push(new MulNode(left, right));
} else if (statementArr[i].equalsIgnoreCase("/")) {
left = (Node) stack.pop();
int val = Integer.parseInt(statementArr[++i]);
right = new ValueNode(val);
stack.push(new DivNode(left, right));
} else if (statementArr[i].equalsIgnoreCase("%")) {
left = (Node) stack.pop();
int val = Integer.parseInt(statementArr[++i]);
right = new ValueNode(val);
stack.push(new ModNode(left, right));
} else {
stack.push(new ValueNode(Integer.parseInt(statementArr[i])));
}
}
this.node = (Node) stack.pop();
}
public int compute() {
return node.interpret();
}
}
2.6、客户端
- Client
public class Client {
public static void main(String[] args) {
String statement = "3 * 4 / 2 % 4";
Calculator calculator = new Calculator();
calculator.build(statement);
int result = calculator.compute();
System.out.println(statement+"="+result);
}
}
五、模式优缺点
1、优点
- 易于改变和扩展文法,由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承机制来改变和扩展文法,实现简单语言方便
- 易于实现文法,在抽象语法树中每一个节点类的实现方式都是相似的,这些类的编写都不是很复杂,它们还可以通过一些工具自动生成
- 增加了新的解释表达式的方式,解释器模式可以让用户较为方便的增加新类型的表达式,增加新的表达式时无需对现有的表达式进行修改,符合开闭原则
2、缺点
- 对于复杂文法难以维护,在解释器模式中,每一条规则至少需要定义一个类,因此如果一个语言包含太多文法规则,则可能难以管理和维护,此时可以考虑使用语法分析程序等方式来取代解释器模式
- 执行效率较低,由于解释器模式中使用了大量的循环和递归调用,因此在解释较为复杂的句子时其速度很慢
- 应用场景很有限,在软件开发中很少需要自定义文法规则,因此该模式的使用频率很低,在一班的软件中难以找到其应用实例,导致理解和使用该模式的难度较大
六、模式适用环境
- 可以将一个需要解释执行的语言中的句子表示为一个抽象语法树
- 一些重复出现的问题可以用一种点单的语言来进行表达
- 文法较为简单,对于复杂的文法,解释器模式中的文法类层次结构将变得很庞大而无法管理,此时最好使用语法分析程序生成器
- 效率不是关键问题,最高效的解释器通常不是通过直接解释语法分析树来实现的,而是需要将它们转换成另一种形式,使用解释器模式的执行效率并不高

浙公网安备 33010602011771号