一、描述
给定一个语言,定义它的文法的一种标识,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。解释器模式为自定义语言的设计和实现提供了一种解决方案,它用于定义一组文法规则并通过这组文法规则来解释语言中的句子。
1.角色:
(1)抽象表达式类(AbstractExpression):定义处理文本数据的抽象方法
(2)终点表达式类(TeminalExpression):实现抽象表达式类,为最终的叶子节点类。
(3)非终点表达式类(UnTeminalExpression):实现抽象表达式类,并非为最终的叶子节点。
(4)上下文(Context):包含变量与值的对应关系。
2.类图:
二、优点
1.易于改变和扩展文法,由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法。
2.每一条文法规则都可以表示为一个类,因此可以方便地实现一个简单的语言。
3.实现文法比较容易。在抽象语法树中每一个表达式节点类的实现方式都是相似的,这些类的代码编写都不会特别复杂,还可以通过一些工具自动生成节点类代码。
4.增加新的解释表达式较为方便。如果用户需要增加新的解释表达式只需要对应增加一个新的终结符表达式或非终结符表达式,原有表达式类代码无需修改,符合“开闭原则”
三、缺点
1.对于复杂文法难以维护。在解释器模式中,每一条规则至少需要定义一个类,因此如果一个语言包含太多文法规则,类的个数将会急剧增加,导致系统难以管理和维护,此时可以考虑使用语法分析程序等方式来取代解释器模式。
2.执行效率较低。由于在解释器模式中使用了大量的循环和递归调用,因此在解释较为复杂的句子时其速度很慢,而且代码的调试过程也比较麻烦。
四、适用场景
1.可以将一个需要解释执行的语言中的句子表示为一个抽象语法树
2.一些重复出现的问题可以用一种简单的语言来进行表达
3.一个语言的文法较为简单
4.执行效率不是关键问题
五、示例
以“计算加减乘除为例”。代码如下:
1.上下文类:
public class Context {
//存储变量与值的对应关系
private Map<String, Integer> map = new HashMap<>();
/**
* 添加值
*
* @param key
* @param value
*/
public void addValue(String key, Integer value) {
map.put(key, value);
}
/**
* 获取值
*/
public Integer getValue(String key) {
return map.get(key);
}
}
2.抽象表达式类
public interface Expression {
/**
* 定义处理上下文的接口
*
* @param context
* @return
*/
Integer interpreter(Context context);
}
3.终端表达式类-变量类
public class Variable implements Expression {
private String key;
/**
* 将变量key值赋值给当前变量
*
* @param key
*/
public Variable(String key) {
this.key = key;
}
/**
* 根据key获取对应的值
*/
@Override
public Integer interpreter(Context context) {
return context.getValue(key);
}
}
4.非终端表达式类-加法表达式
public class Add implements Expression {
private Expression a;
private Expression b;
/**
* 初始化加法符号两侧数据
*
* @param a
* @param b
*/
public Add(Expression a, Expression b) {
this.a = a;
this.b = b;
}
/**
* 处理加法
*
* @param context
* @return
*/
@Override
public Integer interpreter(Context context) {
return a.interpreter(context) + b.interpreter(context);
}
}
5.非终端表达式-减法表达式
public class Minus implements Expression {
private Expression a;
private Expression b;
/**
* 初始化减法符号两侧数据
*
* @param a
* @param b
*/
public Minus(Expression a, Expression b) {
this.a = a;
this.b = b;
}
/**
* 处理减法
*
* @param context
* @return
*/
@Override
public Integer interpreter(Context context) {
return a.interpreter(context) - b.interpreter(context);
}
}
6.非终端表达式-乘法表达式
public class Multiply implements Expression {
private Expression a;
private Expression b;
/**
* 初始化乘法符号两侧数据
*
* @param a
* @param b
*/
public Multiply(Expression a, Expression b) {
this.a = a;
this.b = b;
}
/**
* 初始化乘法符号两侧数据
*/
@Override
public Integer interpreter(Context context) {
return a.interpreter(context) * b.interpreter(context);
}
}
7.非终端表达式-除法表达式
public class Division implements Expression {
private Expression a;
private Expression b;
/**
* 初始化除法符号两侧数据
*
* @param a
* @param b
*/
public Division(Expression a, Expression b) {
this.a = a;
this.b = b;
}
/**
* 处理除法
*/
@Override
public Integer interpreter(Context context) {
return a.interpreter(context) / b.interpreter(context);
}
}
8.测试
public class Client {
public static void main(String[] args) {
//存放变量以及值对应
Context context = new Context();
context.addValue("a", 1);
context.addValue("b", 2);
context.addValue("c", 3);
//存放变量
Variable v1 = new Variable("a");
Variable v2 = new Variable("b");
Variable v3 = new Variable("c");
//将变量添加到运算操作中
//v1+v2
Add add = new Add(v1, v2);
//add - v3 = v1 + v2 - v3
Minus minus = new Minus(add, v3);
//计算
System.out.println("a+b-c=" + minus.interpreter(context));
}
}
测试结果:
大家有没有对这个计算流程感觉迷惑呢?接下来我解释一下这个计算流程。上面计算公式用抽象语法树表示:
计算顺序为:
(1) v1 + v2 = add
(2) add – v3 = v1 + v2 – v3
(3) 最终通过终端表达式v1,v2,v3,将变量与值换算为context中配置的具体数值,从而计算出最终结果。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/158088.html