简述:
解释器模式,给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
解释器模式需要解决的是,如果一种特定类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言的中的句子。这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题。
-
解释器模式(Interpreter Pattern)是指给定一门语言,定义它的文法的一种表示(如:加减乘除表达式和正则表达式等),然后再定义一个解释器,该解释器用来解释我们的文法表示(表达式)。
-
解释器模式是一种按照规定的语法(文法)来进行解析的一种设计模式,属于行为型模式。
结构图:
抽象语法树:
-
抽象语法树(abstract syntax code,AST)是源代码的抽象语法结构的树状表示,树上的每个节点都表示源代码中的一种结构,之所以说是抽象的,抽象表示把js代码进行了结构化的转化,转化为一种数据结构。这种数据结构其实就是一个大的json对象,json我们都熟悉,他就像一颗枝繁叶茂的树。有树根,有树干,有树枝,有树叶,无论多小多大,都是一棵完整的树。
-
抽象语法树(abstract syntax code,AST)是源代码抽象语法结构的树状表示,树上的每个节点都表示源代码中的一种结构,之所以说语法是’抽象’的,是因为这里的语法不会表示出真实语法中出现的每个细节。比如说,嵌套括号被隐含在树的结构中,并没有以节点的形式呈现。
抽象语法树并不依赖于源语言的语法,也就是说语法分析阶段所采用的上下文无文法,因为在写文法时,经常会对文法进行等价的转换(消除左递归,回溯,二义性等),这样会给文法分析引入一些多余的成分,对后续阶段造成不利影响,甚至会使合个阶段变得混乱。因些,很多编译器经常要独立地构造语法分析树,为前端,后端建立一个清晰的接口。
-
什么叫做抽象语法树呢?《java 与模式》中给的解释为:抽象语法树的每一个节点都代表一个语句,而在每个节点上都可以执行解释方法。这个解释方法的执行就代表这个语句被解释。由于每一个语句都代表这个语句被解释。由于每一个语句都代表一个常见的问题的实例,因此每一个节点上的解释操作都代表对一个问题实例的解答。
抽象表达式,声明一个抽象的解释操作,这个接口为抽象语法树中所有的节点所共享。
public abstract class AbstractExpression {
public abstract void interpret(Context context);
}
终结符表达式,实现与文法中的终结符相关联的解释操作。实现抽象表达式中所要求的接口,主要是一个interpret()方法。文法中每一个终结符都有一个具体终结表达式与之相对应。
(终结符表达式(相当于树的叶子):遇到这个表达式interpreter执行能直接返回结果,不会向下继续调用。)
public class TerminalExpression extends AbstractExpression {
public void interpret(Context context) {
System.out.println("终端解释器");
}
}
非终结符表达式,为文法中的非终结符实现解释操作。对文法中每一条规则R1、R2……RN都需要一个具体的非终结符表达式类。通过实现抽象表达式的interpret()方法实现解释操作。解释操作以递归方式调用上面所提到的R1、R2、RN中各个符号的实例变量。
public class NonterminalExpression extends AbstractExpression {
public void interpret(Context context) {
System.out.println("非终端解释器");
}
}
Context,包含解释器之外的一些全局信息。
public class Context {
private String input;
private String output;
public String getInput() {
return input;
}
public void setInput(String input) {
this.input = input;
}
public String getOutput() {
return output;
}
public void setOutput(String output) {
this.output = output;
}
}
客户端代码,构建表示该文法定义的语言中一个特定的句子的抽象语法树。调用解释操作。
public class Client {
public static void main(String[] args) {
Context context = new Context();
List<AbstractExpression> list = new ArrayList<AbstractExpression>();
list.add(new TerminalExpression());
list.add(new NonterminalExpression());
list.add(new TerminalExpression());
list.add(new TerminalExpression());
for (AbstractExpression exp : list) {
exp.interpret(context);
}
}
}
优点:
-
可扩展性比较好,灵活。
-
增加了新的解释表达式的方式
-
解释器模式,可以很容易地改变和扩展文法,因为该模式使用类来表示文法规则,你可使用继承来改变或扩展该文法。也比较容易实现文法,因为定义抽象语法树中各个节点的类的实现大体类似,这些类都易于直接编写。
缺点:
-
当文法规则比较复杂时,会引起类膨胀,比较难维护。
-
当文法规则比较复杂时,如果出错了,调试比较困难。
-
执行效率比较低下。因为当表达式比较复杂,结果层层依赖的话会采用递归方式进行解析。
-
解释器模式为文法中的每一条规则至少定义了一个类,因此包含许多规则的文法可能难以管理和维护。建议当文法非常复杂时,使用其他的技术如语法分析程序或编译器生成器来处理。
适用场景:
-
当有一个语言需要解释执行,并且你可将该语言中的句子表示为一个抽象语法树时,可使用解释器模式。
-
正则表达式就是解释器的一种应用,解释器为正则表达式定义了一个文法,如何表示一个特定的正则表达式,以及如何解释这个正则表达式。
-
当我们有一些需要重复解析的问题可以考虑定义一个表达式来进行表达。
-
一些简单的语法如果需要解释的话也可以考虑解释器模式。
-
可以将一个需要解释执行的语言中的句子表示为一个抽象语法树
-
编译器
-
运算表达式计算、正则表达式
-
机器人