继续整理记录这段时间来的收获,详细代码可在我的Gitee仓库SpringBoot克隆下载学习使用!
6.12 解释器模式
6.12.1 概述
- 思维:翻译识别机器,如解析由数字、“+”、“-”号构成的合法运算序列,若将数字和字符看作结点,可逐个节点进行读取计算
- 定义:给定一种语言,定义文法表示,定义解释器,且此解释器可以使用该标识来解释语言中句子
- 文法(语法)规则:描述语言的语法结构的形式规则,如
expression ::= value|plus|minus
plus::= expression '+' expression
minus::= expression '-' expression
value ::= Integer
可描述为表达式可以为数字或者plus运算或minus运算,而plus、minus又是表达式结合运算符构成,值类型为整数
- 抽象语法树:AbstractSyntaxTree,简称AST,是源代码语法结构的一种抽象表示,以树状形式表示编程语言的语法结构,树上每个节点都表示源代码中的一种结构。如图![[Pasted image 20230118200014.png]]
6.12.2 结构
- 抽象表达式角色(Abstract Expression):定义解释器接口,约定解释器的解释操作,主要包含解释方法Interpret()
- 终结符表达式角色(Terminal Expression):抽象表达式子类,用来实现文法中与终结符相关的操作,文法中的每个终结符都有一个具体终结表达式与之相对应,如value
- 非终结符表达式角色(Noterminal Expression):抽象表达式式子类,用来实现文法中与非终结符相关的操作,文法中的每条规则都对应于一个非终结符表达式,如+、-
- 环境角色(Context):包含各个解释器需要的数据或是公共的功能,一般用来传递被所有解释器共享的数据,后面解释器可以从这里获取值
- 客户端:主要任务是将需要分析的句子或表达式转换成使用解释器对象描述的抽象语法树,然后调用解释器的解释方法,也可以通过环境角色间接访问解释器解释方法
6.12.3 案例(加减法)
- 环境角色
public class Context {
// 用map存储变量
private Map<Variable,Integer> map = new HashMap<Variable,Integer>();
// 添加变量
public void addVariable(Variable v,Integer value){
map.put(v, value);
}
// 获取变量
public int getVariable(Variable v){
return map.get(v);
}
}
- 抽象表达式
public abstract class AbstractExpression {
public abstract int interpret(Context context);
}
- 非终结符表达式
public class Plus extends AbstractExpression{
// + 号左边表达式
private AbstractExpression left;
// + 号右边表达式
private AbstractExpression right;
public Plus(AbstractExpression left,AbstractExpression right) {
this.left = left;
this.right = right;
}
@Override
public int interpret(Context context) {
// 左边表达式+右边表达式
return left.interpret(context) + right.interpret(context);
}
@Override
public String toString() {
return "("+left.toString()+ "+ "+ right.toString() +")";
}
}
public class Minus extends AbstractExpression{
// - 号左边表达式
private AbstractExpression left;
// - 号右边表达式
private AbstractExpression right;
public Minus(AbstractExpression left,AbstractExpression right) {
this.left = left;
this.right = right;
}
@Override
public int interpret(Context context) {
// 左边表达式-右边表达式
return left.interpret(context) - right.interpret(context);
}
@Override
public String toString() {
return "("+left.toString()+ "-"+ right.toString() +")";
}
}
- 终结符表达式
public class Variable extends AbstractExpression{
// 变量名
private String name;
public Variable(String name) {
this.name = name;
}
@Override
public int interpret(Context context) {
return context.getVariable(this);
}
@Override
public String toString() {
return name;
}
}
- 测试
public static void main(String[] args) {
// 创建环境对象
Context context = new Context();
// 创建变量
Variable a = new Variable("a");
Variable b = new Variable("b");
Variable c = new Variable("c");
Variable d = new Variable("d");
// 赋值
context.addVariable(a,1);
context.addVariable(b,3);
context.addVariable(c,7);
context.addVariable(d,4);
// 创建语法规则 a + b - c + d
AbstractExpression expression = new Plus(new Minus(new Plus(a,b),c),d);
int interpret = expression.interpret(context);
System.out.println(expression +"= "+ interpret);
}
- 结果
- 类图
6.12.4 优缺点
6.12.4.1 优点
- 易于改变和扩展文法:由于模式中使用类表示语言文法规则,故可通过继承等机制改变或扩展文法,每一文法规则均可表示为一个类
- 实现文法较为容易:抽象语法树汇总每一表达式节点类实现方式相似
- 增加新解释表达式较为方便:若用户需要增加新解释表达式只需对应增加一新的终结符表达式或非终结符表达式类,原有表达式类不需要修改,符合“开闭原则”。
6.12.4.2 缺点
- 对复杂文法难以维护:每条规则至少定义一个类,若语言包含太多文法规则,类个数爆发增长,导致系统难管理和维护
- 执行效率低:使用了大量循环和递归调用
6.12.5 使用场景
- 当语言文法较为简单,且执行效率不是关键问题
- 当问题重复出现,且可以用一种简单语言来进行表达时
- 当语言需要解释执行,且语言中句子可以表示为一抽象语法树