努力保头发的打工人
努力保头发的打工人
发布于 2020-12-31 / 17 阅读 / 0 评论 / 0 点赞

解释器模式

1 概述

定义 给定一门语言,定义他的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。类图如下:

解释器模式类图.png

  • AbstractExpression 抽象表达式解释器,具体的解释由各个实现类完成,抽象解释器通常只有一个方法,定义每个表达式解释器都有一个解析的任务。任务通过递归调用的方式,最终由最小的语法单元进行解析。

  • TerminalExpression 终结符表达式解释器,现实与文法中的元素关联的解释操作,通常一个解释器模式中只有一个终结符表达式,但是有多个实例,对应不同的终结符。主要处理场景元素和数据转换。

  • NonterminalExpression 非终结符表达式解释器,文法中每条规则对应一个非终结表达式,并且只关心自己周边的文法规则的结果,因此这就产生了每个非终结符表达式调用自己周边的非终结符表达式,然后最终、最小的文法规则就是终结符表达式。

  • context 环境角色 存储表达式元素容器

2 代码示例

已简单的加减法为例,每个变量都是一个终结表达式的元素,每个符号都是一个非终结表达式规则,使用map存储这些元素作为context类,定义表达式解释器抽象类和终结表达式解释器和非终结表达式解释器,其中非终结表达式有加法和减法两个子类:

/**
 * @author chiangtaol
 * @date 2020-12-10
 * @describe 语法抽象类
 */
public abstract class ExpressionAbstract {
​
    // 解析公式和数值,key为公式中的参数,value值是具体数值
    public abstract int interpreter(HashMap<String,Integer> vars);
}
​
/**
 * @author chiangtaol
 * @date 2020-12-10
 * @describe 元素语法解释器
 */
public class VarExpression extends ExpressionAbstract {
​
    /**
     * 公式中的元素
     */
    private String key;
​
    public VarExpression(String key) {
        this.key = key;
    }
​
    /**
     * 从map中获取对应的值
     */
    @Override
    public int interpreter(HashMap<String, Integer> vars) {
        return vars.get(this.key);
    }
}
​
/**
 * @author chiangtaol
 * @date 2020-12-10
 * @describe 公式中运算符号解释器
 */
public abstract class SymbolExpression extends ExpressionAbstract {
​
    protected ExpressionAbstract left;
​
    protected ExpressionAbstract right;
​
    /**
     * 解析公式只关心左右两个表达式结果
     */
    public SymbolExpression(ExpressionAbstract left, ExpressionAbstract right) {
        this.left = left;
        this.right = right;
    }
}
​
/**
 * @author chiangtaol
 * @date 2020-12-10
 * @describe 加法解释器
 */
public class AddExpression extends SymbolExpression{
​
    public AddExpression(ExpressionAbstract left,ExpressionAbstract right){
        super(left, right);
    }
​
    /** 相加*/
    @Override
    public int interpreter(HashMap<String, Integer> vars) {
        return super.left.interpreter(vars)+super.right.interpreter(vars);
    }
}
​
/**
 * @author chiangtaol
 * @date 2020-12-10
 * @describe 减法解释器
 */
public class SubExpression extends SymbolExpression {
​
    public SubExpression(ExpressionAbstract left, ExpressionAbstract right) {
        super(left, right);
    }
​
    /**
     * 相减
     */
    @Override
    public int interpreter(HashMap<String, Integer> vars) {
        return super.left.interpreter(vars) - super.right.interpreter(vars);
    }
}

创建场景类,封装表达式的调用

/**
 * @author chiangtaol
 * @date 2020-12-10
 * @describe 封装解释器调用的类
 */
public class Calculator {
​
    private ExpressionAbstract expressionAbstract;
​
    /** 构造函数中解析传入字符表达式*/
    public Calculator(String string){
        // 定义一个栈,安排运算的先后顺序
        Stack<ExpressionAbstract> stack = new Stack<>();
        // 将表达式拆为数组
        char[] chars = string.toCharArray();
        // 运算
        ExpressionAbstract left = null;
        ExpressionAbstract right = null;
        for (int i = 0; i < chars.length; i++) {
            switch (chars[i]){
                case '+':
                    left = stack.pop();
                    right = new VarExpression(String.valueOf(chars[++i]));
                    stack.push(new AddExpression(left,right));
                    break;
                case '-':
                    left =stack.pop();
                    right = new VarExpression(String.valueOf(chars[++i]));
                    stack.push(new SubExpression(left,right));
                    break;
                default:
                    stack.push(new VarExpression(String.valueOf(chars[i])));
            }
        }
        this.expressionAbstract = stack.pop();
    }
​
​
    // 运算结果
    public int run(HashMap<String,Integer> var){
        return this.expressionAbstract.interpreter(var);
    }
​
    public static void main(String[] args) throws Exception{
        // 表达式
        String expressionStr = getExpressionStr();
        // 表达式中的数值
        HashMap<String, Integer> value = getValue(expressionStr);
        // 计算
        Calculator calculator = new Calculator(expressionStr);
        System.out.println("结果:"+expressionStr+"="+calculator.run(value));
    }
​
​
    /** 获取表达式*/
    public static String getExpressionStr() throws Exception{
        System.out.println("请输入表达式");
        return (new BufferedReader(new InputStreamReader(System.in))).readLine();
    }
​
    /** 获取表达式中数值*/
    public static HashMap<String,Integer> getValue(String var) throws Exception{
        HashMap<String,Integer> hashMap = new HashMap<>();
        for (char ch : var.toCharArray()){
            if (ch != '+' && ch != '-'){
                if (!hashMap.containsKey(String.valueOf(ch))){
                    System.out.println("请输入"+ch+"的值");
                    String in = (new BufferedReader(new InputStreamReader(System.in))).readLine();
                    hashMap.put(String.valueOf(ch),Integer.valueOf(in));
                }
            }
        }
        return hashMap;
    }
}

测试主方法运行结果如下:

请输入表达式 a+b-c-d+e

请输入a的值

40

请输入b的值

40

请输入c的值

20

请输入d的值

20

请输入e的值

0

结果:a+b-c-d+e=40

3 优缺点

3.1 优点

是一个简单的语法分析工具,它最显著的有点就是扩展性,修改语法规则只要修改相应的非中介符表达式就可以了,若扩展语法,则只要增加非中介符类就可以了。

3.2 缺点
  • 解释器模式会引起类膨胀

  • 采用了递归调用方法,问题不好排查

  • 使用的大量的循环和递归,效率是个不容忽视的问题

4 使用场景

  • 重复发生问题可以使用解释器模式,如:大量日志,格式不同,但是数据要素相同,非终结表达式不同而已

  • 一个简单的语法需要解释的场景,比较标准的字符集

一般在项目中使用较少,注意不要在重要模块使用解释器模式,维护会成为一个很大的问题。