1.简要概述
- 解释器模式是一种用得比较少的设计模式,属于行为型设计模式。
- 解释器模式提供了一种解释语言的语法或表达式的方式,该模式定义了一个表达式接口,通过该接口解释一个特定的上下文。
- 解释器模式用于描述如何构成一个简单的语言解释器,主要用于使用面向对象语言开发的编译器和解释器设计。
- 解释器模式的核心思想就是为给定的语言创建解释器,然后对语言中的语法进行分析,定义出它的文法的一种表示,最后通过解释器来解释运行语言中的句子。
2.模式结构
👉通常由一个上下文环境类(
负责定义一些解释器之外的一些全局信息
),一个抽象表达式类(负责声明一个抽象的解释操作,供语法树中的所有节点使用
),一个终结符表达式类(负责实现与语法中的终结符相关的解释操作
),一个非终结符表达式类(负责实现与语法中的非终结符相关的解释操作
),一个客户类( `负责对表达式类进行调用,完成语言的解释操作)共同组成。
3.实现代码
举例 💡 :用解释器模式实现四则运算表达式的解释执行
表达式抽象类
public abstract class Expression {
// 解释器抽象方法
public abstract int interpret(HashMap<String, Integer> valueMap);
}
变量解释器(终结符表达式类)
public class VarExpression extends Expression{
private String key;
public VarExpression(String key) {
this.key = key;
}
// 变量取值解释器
@Override
public int interpret(HashMap<String, Integer> valueMap) {
return valueMap.get(key);
}
}
加法运算符解释器(非终结符表达式类)
public class AddExpression extends Expression{
private Expression var1;
private Expression var2;
public AddExpression(Expression var1, Expression var2) {
this.var1 = var1;
this.var2 = var2;
}
// 加法逻辑解释器
@Override
public int interpret(HashMap<String, Integer> valueMap) {
return var1.interpret(valueMap) + var2.interpret(valueMap);
}
}
减法运算符解释器(非终结符表达式类)
public class SubExpression extends Expression{
private Expression var1;
private Expression var2;
public SubExpression(Expression var1, Expression var2) {
this.var1 = var1;
this.var2 = var2;
}
// 减法逻辑解释器
@Override
public int interpret(HashMap<String, Integer> valueMap) {
return var1.interpret(valueMap) - var2.interpret(valueMap);
}
}
计算器类(上下文环境类)
public class Calculator {
private Expression expression;
public Calculator(String express) {
Stack<Expression> stack = new Stack<>();
char[] symbols = express.toCharArray();
// 表达式解析执行
Expression var1 = null;
Expression var2 = null;
for (int i = 0; i < symbols.length; i++) {
switch (symbols[i]) {
case '+':
var1 = stack.pop();
var2 = new VarExpression(String.valueOf(symbols[++i]));
stack.push(new AddExpression(var1, var2));
break;
case '-':
var1 = stack.pop();
var2 = new VarExpression(String.valueOf(symbols[++i]));
stack.push(new SubExpression(var1, var2));
break;
default:
stack.push(new VarExpression(String.valueOf(symbols[i])));
break;
}
}
this.expression = stack.pop();
}
public int excute(HashMap<String, Integer> valueMap) {
return expression.interpret(valueMap);
}
}
客户类
// 测试客户端
public class CompanyClient{
public static void main(String[] args) {
// 定义一个表达式
String express = "a+b-c";
// 创建计算器对表达式进行解析
Calculator calculator = new Calculator(express);
// 计算指定数据的运算结果
HashMap<String, Integer> valueMap = getValue(express);
System.out.println(express + "=" + calculator.excute(valueMap));
}
public static HashMap<String, Integer> getValue(String express){
HashMap<String, Integer> result = new HashMap<>();
Scanner scanner = new Scanner(System.in);
for (char c : express.toCharArray()) {
if(c != '+' && c != '-') {
System.out.println("请输入变量" + c + "的值:");
int value = scanner.nextInt();
result.put(String.valueOf(c), Integer.valueOf(value));
}
}
return result;
}
}
4.优点好处
- 解释器模式具有灵活的扩展性,当我们想对文法规则进行扩展延伸时,只需要增加相应的非终结符表达式解释器即可。
- 提供了一种新的解释表达式的方式,并且容易实现一些简单文法的解释。
5.缺点弊端
- 解释器模式中,每一条语法的解析都会对应着一个解释器,因此系统中会生成大量的类,增加了系统的维护难度。
- 解释器模式采用递归调用的方法,完整表达式的最终结果是通过从后往前递归调用所有的解释器中的方法获取得到的,因此可能会导致程序的执行效率降低。
6.应用场景
- 当我们需要开发一种新的语言,然后创建该语言的语法解析树的时候,就可以使用解释器模式进行处理。
- 当我们想进行正则表达式解析、SQL语法解析、制作编译器、进行运算表达式计算等场景时,就可以使用解释器模式进行处理。
- 在某些特定的领域出现不断重复的问题时,可以将该领域的问题转化为一种语法规则下的语句,然后构建解释器来解释该语句。
7.应用示例
JDK源码中的ELResolver类型解析器的解析过程
-
这里的ELResolver抽象解析器类就可以看做是解释器模式中的抽象表达式类,里面定义了一系列的解析抽象方法,比如下面的setValue方法。
-
可以看到ELRsolver抽象类有很多具体的解析器实现,每种解析器都针对于不同的类型进行解析,就好比是解释器模式中对于不同语法的解析。
-
以下面的ListELRsolver实现类举例,它继承了ELRsolver类并实现了setValue方法,我们可以把它看作是表达式抽象类的一个具体实现。
-
这里的StandardELContext类就好比是解释器模式中的上下文环境类,我们通过它可以获取一个指定的Resolver解析器。
-
通过上述分析过程可以看出,ELRsolver这个解析器类的解析过程就采用了解释器模式进行实现。