给定一个语言,定义它的文法的一种标识,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。解释器模式为自定义语言的设计和实现提供了一种解决方案,它用于定义一组文法规则并通过这组文法规则来解释语言中的句子。
(1)抽象表达式类(AbstractExpression):定义处理文本数据的抽象方法
(2)终点表达式类(TeminalExpression):实现抽象表达式类,为最终的叶子节点类。
(3)非终点表达式类(UnTeminalExpression):实现抽象表达式类,并非为最终的叶子节点。
(4)上下文(Context):包含变量与值的对应关系。
1.易于改变和扩展文法,由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法。
2.每一条文法规则都可以表示为一个类,因此可以方便地实现一个简单的语言。
3.实现文法比较容易。在抽象语法树中每一个表达式节点类的实现方式都是相似的,这些类的代码编写都不会特别复杂,还可以通过一些工具自动生成节点类代码。
4.增加新的解释表达式较为方便。如果用户需要增加新的解释表达式只需要对应增加一个新的终结符表达式或非终结符表达式,原有表达式类代码无需修改,符合“开闭原则”
1.对于复杂文法难以维护。在解释器模式中,每一条规则至少需要定义一个类,因此如果一个语言包含太多文法规则,类的个数将会急剧增加,导致系统难以管理和维护,此时可以考虑使用语法分析程序等方式来取代解释器模式。
2.执行效率较低。由于在解释器模式中使用了大量的循环和递归调用,因此在解释较为复杂的句子时其速度很慢,而且代码的调试过程也比较麻烦。
1.可以将一个需要解释执行的语言中的句子表示为一个抽象语法树
2.一些重复出现的问题可以用一种简单的语言来进行表达
3.一个语言的文法较为简单
4.执行效率不是关键问题
以“计算加减乘除为例”。代码如下:
- 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);
- }
- }
-
- public interface Expression {
- /**
- * 定义处理上下文的接口
- *
- * @param context
- * @return
- */
- Integer interpreter(Context context);
- }
-
- 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);
- }
- }
-
- 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);
- }
- }
-
- 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);
- }
- }
-
- 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);
- }
- }
-
- 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);
- }
- }
-
- 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中配置的具体数值,从而计算出最终结果。