java 设计模式之解释器模式(十九)

163 阅读5分钟

java 设计模式之解释器模式①⑨

躁动的心灵,不安的灵魂。在彷徨中挣扎,在迷失中探索。时光飞逝,努力不变。

设计模式学习,近期我会把23种设计模式都写成博客,敬请期待~
—2021/1/26

定义

定义了一个解释器,来解释给定语言和文法的句子。其实质是把语言中的每个符号定义成一个(对象)类,从而把每个程序转换成一个具体的对象树.(编译原理上的编译器)

百度百科

举例理解:
要想和外国人交流,就得通过他们的语言,比如’你好’,把你好传递给翻译官,翻译官翻译出来之后告诉外国人,这里的翻译官就扮演了解释器的责任.

角色分析

UML类图(1.1):
在这里插入图片描述

  • 抽象解释器(Expression) :具体的解释任务由各个实现类完成。
  • 终结符表达式(VarExpression) :实现与文法中的元素相关联的解释操作,通常一个解释器模式中只有一个终结表达式,但有多个实例,对应不同的终结符。
  • 非终结符表达式(SymbolExpression) :文法中的每条规则对应于一个非终结表达式,非终结符表达式根据逻辑的复杂程度而增加,原则上每个文法规则都对应一个非终结符表达式
  • 上下文(Calculator) : 上下文环境类,包含解释器之外的全局信息
  • 客户类: 客户端,解析表达式,构建抽象语法树,执行具体的解释操作等.

注:
这里的角色不是固定的,以实际场景来确定

非终结符表达式和终结符表达式区别:

  • 非终结符表达式(相当于树的树杈),还会向下执行
  • 终结符表达式(相当于树叶子),不会向下执行

使用场景

  • 一些重复出现的问题可以用一种简单的语言来表达,
  • 一个简单语法需要解释的场景

正则表达式就是采用的解释器模式,但是解释器模式相对于其他的设计模式来说,使用还是太少了!,了解即可.

代码实现

Expression(抽象解释器):

public abstract class Expression {

    /**
     *
     * @param var key: a,b,c  |  value: 1,2,3...
     */
    public abstract int interpreter(HashMap<String,Integer> var);
}

SymbolExpression非终结符表达式:

public abstract class SymbolExpression extends Expression {

    protected Expression left;

    protected Expression right;

    //所有的解析公式都应只关心自己左右两个表达式的结果
    public SymbolExpression(Expression _left,Expression _right){
        this.left = _left;
        this.right = _right;
    }
}

这个类只关心运算符左右的值,比如a + b,他只关心 + 号左右的 a 和 b ,具体运算交给其子类

AddExpression(加法运算符):

public class AddExpression extends SymbolExpression {

    public AddExpression(Expression _left, Expression _right) {
        super(_left, _right);
    }

    //把左右两个表达式运算的结果加起来
    @Override
    public int interpreter(HashMap<String, Integer> var) {
        return super.left.interpreter(var) + super.right.interpreter(var);
    }
}

通过父类(SymbolExpression)中的left和right相加即可

SubExpression(减法运算符):

public class SubExpression extends SymbolExpression {

    public SubExpression(Expression _left,Expression _right){
        super (_left,_right);
    }
    //左右两个表达式相减
    @Override
    public int interpreter(HashMap<String, Integer> var) {
        return super.left.interpreter(var) - super.right.interpreter(var);
    }
}

通过父类(SymbolExpression)中的left和right相加即可

VarExpression(终结表达式,没有子类!)

public class VarExpression extends Expression {

    private String key;

    public VarExpression(String _key){
        this .key = _key;
    }
    //从map中取之
    @Override
    public int interpreter(HashMap<String, Integer> var) {
        return var.get( this.key);
    }
}

这里传递的_key就是对应的字母,假设现在求a+b,这里的_key就是a,b

Calculator获取表达式,计算结果(这个类是关键!)

public class Calculator {

    //定义的表达式
    private Expression expression;
    //构造函数传参,并解析

    public Calculator(String expStr) {
        //定义一个堆栈,安排运算的先后顺序
        Stack<Expression> stack = new Stack<Expression>();
        //表达式拆分为字符数组
        char[] charArray = expStr.toCharArray();
        //运算
        Expression left = null;
        Expression right = null;
        for (int i = 0; i < charArray.length; i++) {
            switch (charArray[i]) {
                case '+': //加法
                    //加法结果放到堆栈中
                    left = stack.pop();
                    right = new VarExpression(String.valueOf(charArray[++i]));
                    stack.push(new AddExpression(left, right));
                    break;
                case '-':
                    left = stack.pop();
                    right = new VarExpression(String.valueOf(charArray[++i]));
                    stack.push(new SubExpression(left, right));
                    break;
                default: //公式中的变量
                    stack.push(new VarExpression(String.valueOf(charArray[i])));
            }
        }
        //把运算结果抛出来
        this.expression = stack.pop();
    }

    //开始运算
    public int run(HashMap<String, Integer> var) {
        return this.expression.interpreter(var);
    }
}

分析:

假设现在表达式还是:a+b

当创建Calculator(String expStr)时,传入的就是"a+b"

然后把"a+b"拆分成[a , + , b] 数组,让其来循环

第一次循环 a ,使用来压入栈底

第二次循环 + 号,首先执行left = stack.pop();方法,因为当前栈内只有一个,所以此时left等于a,
然后把它赋值给终结者表达式VarExpression(String.valueOf(charArray[++i])) 此时 i == 3,然后把a和b赋值给加法表达式(AddExpression())让他相加即可

所以这里a+b,只循环2次!

java Stack()栈的使用

测试代码(客户端):

		   解释器模式 经典案例   \\\\\\\\\\\\\\\\\\\\\\\
        HashMap<String, Integer> map = new HashMap<>();
        map.put("a",23);
        map.put("b",12);

        Calculator cal = new Calculator("a+b");

        System.out.println( "运算结果为:" + cal.run(map));

Log图(2.1):
在这里插入图片描述

总结:

  • 优点:

    • 扩展性强,若要新增乘,除,添加相应的非终结表达式,修改计算逻辑即可。
  • 缺点

    • 需要建大量的类,因为每一种语法都要建一个非终结符的类。
    • 解释的时候采用递归调用方法,导致有时候函数的深度会很深,影响效率。

总得来说,解释器模式用的比较少,理解即可.

完整代码

去设计模式/设计原则主页

原创不易,您的点赞就是对我最大的支持~