意图
给定一个语言,定义他的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
例子
那么根据结构建立一个简单的解释器public abstract class Expression {
public abstract int interpreter(HashMap<String, Integer> data);
}
参数解析public class VarExpression extends Expression {
private String key;
public VarExpression(String key) {
this.key = key;
}
public int interpreter(HashMap<String, Integer> data) {
return data.get(this.key);
}
}
以及符号解析,具备了符号左右的参数添加public abstract class SymbolExpression extends Expression {
protected Expression leftVar, rightVar;
public SymbolExpression(Expression leftVar, Expression rightVar) {
this.leftVar = leftVar;
this.rightVar = rightVar;
}
public abstract int interpreter(HashMap<String, Integer> data);
}
首先准备一个加法符号public class AddExpression extends SymbolExpression {
public AddExpression(Expression leftVar, Expression rightVar) {
super(leftVar, rightVar);
}
public int interpreter(HashMap<String, Integer> data) {
return super.leftVar.interpreter(data) + super.rightVar.interpreter(data);
}
}
接下来只需要使用配置的对象解决问题即可
适用性
当有一个语言需要解释执行,并且可以将该语言中的句子表示为一个抽象语法树时,可使用解释器模式。
- 该文法简单对于复杂的文法,文法的类层次变得庞大而无法管理。此时语法分析程序生成器这样的工具是更好的选择。他们无需构建抽象语法树即可解释表达式,这也可以节省空间而且还可能节省时间。
- 效率不是一个关键问题最高效的解释器通常不是通过直接解释语法分析树实现的,而是首先将她们转换成另一个形式。例如,正则表达式通常被转换成状态机。但即使在这种情况下,转换器仍可用解释器模式实现,该模式仍是有用的。
结构
协作
- Client 构建 ( 或被给定 )一个句子 , 它是 NonterminalExpression和TerminalExpression的实例的一个抽象语法树. 然后初始化上下文并调用解释操作。
- 每一非终结符表达式节点定义相应子表达式的解释操作。而各终结符表达式的解释操作构成了递归的基础。
- 每一节点的解释操作用上下文来存储和访问解释器的状态。
效果
解释器模式有下列的优点和不足 :
- 易于改变和扩展文法
因为该模式使用类来表示文法规则 , 你可使用继承来改变或扩展该文法。已有的表达式可被增量式地改变 ,而新的表达式可定义为旧表达式的变体。 - 也易于实现文法
定义抽象语法树中各个节点的类的实现大体类似。这些类易于直接编写,通常它们也可用一个编译器或语法分析程序生成器自动生成。 - 复杂的文法难以维护
解释器模式为文法中的每一条规则至少定义了一个类 ( 使用BNF定义的文法规则需要更多的类 )。因此包含许多规则的文法可能难以管理和维护。可应用其他的设计模式来缓解这一问题。但当文法非常复杂时, 其他的技术如语法分析程序或编译器生成器更为合适。 - 增加了新的解释表达式的方式
解释器模式使得实现新表达式“计算”变得容易。 例如, 你可以在表达式类上定义一个新的操作以支持优美打印或表达式的类型检查。如果你经常创建新的解释表达式的方式 , 那么可以考虑使用访问者模式以避免修改这些代表文法的类。