定义
解释器模式(Interpreter Pattern):给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
结构
- AbstractExpression(抽象语法):在抽象表达式中声明了抽象的解释操作,它是所有终结符表达式和非终结表达式的公共父类。
- TerminalExpression(终结符表达式):终结符表达式是抽象表达式的子类,它实现了与文法中的终结符相关联的解释操作,在句子中的每一个终结符都是该类的一个实例。通常在一个解释器模式中只有少数几个终结符表达式类,他们的实例可以通过非终结符表达式组成较为复杂的句子。
- NonterminalExpression(非终结符表达式):非终结符表达式也是抽象表达式的子类,它实现了文法中非终结符的解释操作,由于在非终结符表达式中可以包含终结符表达式,也可以继续包含非终结符表达式,因此其解释操作一般通过递归的方式完成。
- Context(环境类):环境类又称为上下文类,它用于储存解释器之外的一些全局信息,通常它临时存储了需要解释的语句。
代码
Context
import java.util.HashMap;
import java.util.Map;
public class Context {
private Map<String, String> map = new HashMap<>();
public void assign(String key, String value) {
// 往环境类中设值
map.put(key, value);
}
public String lookup(String key) {
// 获取储存在环境类中的值
return map.get(key);
}
}
AbstractExpression
public abstract class AbstractExpression {
public abstract void interpret(Context context);
}
TerminalExpression
public class TerminalExpression extends AbstractExpression {
@Override
public void interpret(Context context) {
// 终结符表达式的解释操作
System.out.println("TerminalExpression");
}
}
NonterminalExpression
public class NonterminalExpression extends AbstractExpression {
private AbstractExpression left;
private AbstractExpression right;
public NonterminalExpression(AbstractExpression left, AbstractExpression right) {
this.left = left;
this.right = right;
}
@Override
public void interpret(Context context) {
// 递归调用每一个组成部分的interpret()方法
// 在递归调用时指定组成部分的连接方式,即非终结符的功能
}
}
优/缺点与适用环境
- 优点
- 易于改变和扩展文法。由于在解释器模式中使用类表示语言的文法规则,因此可以通过集成等机制来改变或扩展文法。
- 没一条文法规则都可以表示为一个类,因此可以方便地实现一个简单的语言。
- 实现文法较为容易。在抽象语法树中每一个表达式结点类的实现方式都是相似的,这些类的代码编写都不会特别复杂,还可以通过一些工具自动生成结点类代码。
- 增加新的解释表示式方式较为方便。如果用户要增加新的解释表示式只需要对应增加一个新的终结符表达式或非终结符表达式类,原有表达式类代码无须修改,符合开闭原则。
- 缺点
- 对于复杂的文法难以维护。在解释器模式中每一条规则至少需要定义一个类,因此如果一个语言包含太多文法规则,类的个数将会急剧增加,导致系统难以管理和维护,此时可以考虑使用语法分析程序等方式来取代解释器模式。
- 执行效率低。由于在解释器模式中使用了大量的循环和递归调用,因此在解释较为复杂的句子时其速度很慢,而且代码的调试过程也比较麻烦。
- 适用环境
- 可以将一个需要解释执行的语言中的句子表示为一颗抽象语法树。
- 一些重复出现的问题可以用一种简单的语言进行表达。
- 一个语言的文法较为简单。对于复杂的文法,解释器模式中的文法类层次结构将变得很庞大而无法管理,此时最好使用语法分析程序生成器。
- 执行效率不是关键问题。高效的解释器通常不是通常直接解析抽象语法树来实现的,而是需要将他们转换成其他形式,使用解释器模式的执行效率并不高。