今天我们来学习解释器模式,比起命令模式,解释器模式更加小众,只会在一些特定的领域会被用到,比如编译器、规则引擎、正则表达式。所以,解释器模式也不是学习的重点,稍微了解一下。
概述
解释器模式:(Interpreter Design Pattern)解释器模式为某个语言定义它的语法(或者叫文法)表示,并定义一个解释器用来处理这个语法。
今天要学习的解释器模式,其实就是用来实现根据语法规则解读“句子”的解释器。
何时使用:
- 如果一种特定类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子。这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题。
UML 类图:
角色组成:
-
AbstractExpression:
定义解释器的接口,约定解释器的解释操作
。 -
TerminalExpression:
终结符解释器
,用来实现语法规则中和终结符相关的操作,不再包含其它的解释器
,如果用组合模式来构建抽象语法树的话,就相当于组合模式中的叶子对象,可以有多种终结符解释器
。 -
NonterminalExpression:
非终结符解释器
,用来实现语法规则中非终结符相关的操作,通常一个解释器对应一个语法规则,可以包含其它的解释器
,如果用组合模式来构建抽象语法树的话,就相当于组合模式中的组合对象,可以有多种非终结符解释器
。 -
Context:
上下文,通常包含各个解释器需要的数据,或是公共的功能
。 -
Client:客户端,
指的是使用解释器的客户端
,通常在这里去把按照语言的语法做的表达式,转换成为使用解释器对象描述的抽象语法树,然后调用解释操作。
通用代码
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 abstract class AbstractExpression {
/**
* 解释的操作
* @param ctx 上下文对象
*/
public abstract void interpret(Context ctx);
}
终结符表达式
/**
* 终结符表达式
*/
public class TerminalExpression extends AbstractExpression{
public void interpret(Context ctx) {
//实现与语法规则中的终结符相关联的解释操作
System.out.println("终结符表达式");
}
}
非终结符表达式
/**
* 非终结符表达式
*/
public class NonterminalExpression extends AbstractExpression{
public void interpret(Context ctx) {
//实现与语法规则中的非终结符相关联的解释操作
System.out.println("非终结符表达式");
}
}
Client客户端
public class Client {
public static void main(String[] args) {
Context context = new Context();
List<AbstractExpression> list = new ArrayList<>();
list.add(new TerminalExpression());
list.add(new NonterminalExpression());
list.add(new TerminalExpression());
for (AbstractExpression abstractExpression : list) {
abstractExpression.Interpret(context);
}
}
}
其中list为一个语法容器,容纳一个具体的表达式。通常Client是一个封装类,封装的结果就是传递进来一个规范语法文件,解析器分析后产生结果并返回,避免了调用者与语法分析器的耦合关系。
结果:
终结符表达式
非终结符表达式
终结符表达式
总结
为什么会有这个解释器模式呢,从编译原理中可以受到一些启发,使用了这样的一个解释器可以在Java语言之上再定义一层语言,这种语言通过Java编写的解释器可以放到Java环境中去执行,这样如果用户的需求发生变化,比如打算做其他事情的时候,只用在自己定义的新的语言上进行修改,对于Java编写的代码不需要进行任何的修改就能在Java环境中运行,这是非常有用的。这就好像,虽然不管怎么编译,最终由中间代码生成最终代码(机器码)是依赖于相应的机器的。但是编译器却能理解高级语言和低级语言,无论高级语言的程序是怎么样编写的,编译器的代码是不用修改的,而解释器模式就是想做一个建立在Java和我们自定义语言之间的编译器。
解释器模式的代码实现比较灵活,没有固定的模板。我们前面说过,应用设计模式主要是应对代码的复杂性,解释器模式也不例外。它的代码实现的核心思想,就是将语法解析的工作拆分到各个小类中,以此来避免大而全的解析类。一般的做法是,将语法规则拆分一些小的独立的单元,然后对每个单元进行解析,最终合并为对整个语法规则的解析。