解释器模式(Interpreter Pattern)提供了评估语言的语法或表达式的方式,它属于行为型模式。这种模式实现了一个表达式接口,该接口解释一个特定的上下文。这种模式被用在 SQL 解析、符号处理引擎等。
解释器模式一般分为五类:
- 抽象解释器(AbstractExpression):具体的解释任务由各个实现类完成。
- 终结符表达式(TerminalExpression):实现与文法中的元素相关联的解释操作,通常一个解释器模式中只有一个终结表达式,但有多个实例,对应不同的终结符。
- 非终结符表达式(NonterminalExpression):文法中的每条规则对应于一个非终结表达式,非终结符表达式根据逻辑的复杂程度而增加,原则上每个文法规则都对应一个非终结符表达式
- 上下文(Context): 上下文环境类,包含解释器之外的全局信息
- 客户类(Test): 客户端,解析表达式,构建抽象语法树,执行具体的解释操作等.
一般我们除了SQL的定义,在很多别的地方也会看到,比如定义一个新的公式来进行运算,定义符号对应操作等等。
=========================================================================
我们来模仿定义写一个加减函数:
输入一个模型公式(加减四则运算),然后输入模型中的参数,运算出结果。
定义一个解释器接口:
package BehavioralPatterns.InterpreterPattern;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName Expression.java
* @Description 解释器接口
* @createTime 2022年03月18日 16:11:00
*/
public interface Expression {
/**
* 解释器
* @param context 上下文
* @return 结果值
*/
int interpreter(Context context);
}
定义上下文:
package BehavioralPatterns.InterpreterPattern;
import java.util.HashMap;
import java.util.Map;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName Context.java
* @Description 上下文
* @createTime 2022年03月18日 16:12:00
*/
public class Context {
private Map<Expression, Integer> map = new HashMap<>();
/**
* 定义变量
* @param s 传入变量
* @param value 对应的integer
*/
public void add(Expression s, Integer value){
map.put(s, value);
}
/**
* 将变量转换成数字
* @param s 传入的变量
* @return 对应的数字
*/
public int changeToNumber(Expression s){
return map.get(s);
}
}
定义抽象非终结符表达式:
package BehavioralPatterns.InterpreterPattern;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName NonTerminalExpression.java
* @Description 抽象非终结符表达式
* @createTime 2022年03月18日 16:13:00
*/
public abstract class NonTerminalExpression implements Expression {
Expression e1,e2;
public NonTerminalExpression (Expression e1, Expression e2){
this.e1 = e1;
this.e2 = e2;
}
}
减法表达式实现类:
package BehavioralPatterns.InterpreterPattern;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName MinusOperation.java
* @Description 减法表达式实现类
* @createTime 2022年03月18日 16:15:00
*/
public class MinusOperation extends NonTerminalExpression{
public MinusOperation(Expression e1, Expression e2) {
super(e1, e2);
}
/**
* 返回相减的值
* @param context 上下文
* @return 相减之后的值
*/
@Override
public int interpreter(Context context) {
System.out.println(e1.interpreter(context) +"-" + e2.interpreter(context));
return e1.interpreter(context) - e2.interpreter(context);
}
}
加法表达式实现类:
package BehavioralPatterns.InterpreterPattern;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName PlusOperation.java
* @Description 加法表达式实现类
* @createTime 2022年03月18日 16:25:00
*/
public class PlusOperation extends NonTerminalExpression{
public PlusOperation(Expression e1, Expression e2) {
super(e1, e2);
}
@Override
public int interpreter(Context context) {
System.out.println(e1.interpreter(context) +"+" + e2.interpreter(context));
return e1.interpreter(context) + e2.interpreter(context);
}
}
终结符表达式:
package BehavioralPatterns.InterpreterPattern;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName TerminalExpression.java
* @Description 终结符表达式
* @createTime 2022年03月18日 16:17:00
*/
public class TerminalExpression implements Expression{
String var;
public TerminalExpression (String var){
this.var = var;
}
@Override
public int interpreter(Context context) {
return context.changeToNumber(this);
}
}
测试一下:
package BehavioralPatterns.InterpreterPattern;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName InterpreterTest.java
* @Description 测试类
* @createTime 2022年03月18日 16:21:00
*/
public class InterpreterTest {
public static void main(String[] args) {
Context context = new Context();
TerminalExpression a = new TerminalExpression("a");
TerminalExpression b = new TerminalExpression("b");
TerminalExpression c = new TerminalExpression("c");
context.add(a,1);
context.add(b,2);
context.add(c,3);
System.out.println("="+new MinusOperation(new PlusOperation(a,b),c).interpreter(context));
}
}
优点:
解释器是一个简单语法分析工具,它最显著的优点就是扩展性,修改语法规则只要修改相应的非终结符表达式就可以了,若扩展语法,则只要增加非终结符类就可以了。
缺点:
- 解释器模式会引起类膨胀
每个语法都要产生一个非终结符表达式,语法规则比较复杂时,就可能产生大量的类文件,为维护带来了非常多的麻烦。
- 解释器模式采用递归调用方法
每个非终结符表达式只关心与自己有关的表达式,每个表达式需要知道最终的结果,必须一层一层地剥茧,无论是面向过程的语言还是面向对象的语言,递归都是在必要条件下使用的,它导致调试非常复杂。想想看,如果要排查一个语法错误,我们是不是要一个一个断点的调试下去,直到最小的语法单元。
- 效率问题
解释器模式由于使用了大量的循环和递归,效率是个不容忽视的问题,特别是用于解析复杂、冗长的语法时,效率是难以忍受的。