美文网首页
设计模式《解释器模式》

设计模式《解释器模式》

作者: 天道__ | 来源:发表于2018-08-16 11:31 被阅读0次

    引言

      看到这个模式,第一反应是我没见过。在看看定义,太抽象了,所以放到后面写。首先我们先看看上一节命令模式,继续学学这节的解释器模式。

    示例地址

      Demo(simple2 感觉不是很好的说明解释器模式)

    类图

    image

    定义

      定义一个语言的文法,并且建立一个解释器来解释该语言中的句子,这里的“语言”是指使用规定格式和语法的代码。

    使用场景

      1. 如果某个简单的语言需要解释执行而且可以将该语言中的语句表叔为一个抽象语法树时可以考虑使用解释器模式。
      2. 在某些特定的领域出现不断重复的问题时,可以将该领域的问题转化为一种语法规则下的语句,然后构建解释器来解释该语句。

    解释器模式中的概念词

    文法规则:

      一种简单的语言定义。例如表达"1+ 2 + 3 – 4 + 1".

    expression = value | operation
    operation = '+' | '-'
    value = an integer //一个整数值

    其中整个语句为表达式。表达式有 单一的表达式、复合的表达式(多个单一的表达式组合而成,通过非终结符操作),"+" | "-"为非终结符。value 为终结符。

    抽象语法树

      例如表达"1+ 2 + 3 – 4 + 1".从下往上看。


    image
    终结符表达式:

      比如有一个简单的公式R=R1+R2,在里面R1和R2就是终结符,对应的解析R1和R2的解释器就是终结符表达式。

    非终结符表达式

      比如公式R=R1+R2中,+就是非终结符,解析+的解释器就是一个非终结符表达式。

    Context(环境类)

      环境类又称为上下文类,它用于存储解释器之外的一些全局信息,通常它临时存储了需要解释的语句。

    解释器模式简单示例

      也许上面的你没看懂,下面我们通过代码来展示上面“1+ 2 + 3 – 4 + 1”的含义。

    1. 抽象解析类
    /**
     * 抽象解析类
     *
     * @author 512573717@qq.com
     * @created 2018/8/14  下午5:13.
     */
    public abstract class AbstractExpression {
        public abstract int interpret();
    }
    
    2. 数字的实现(终结符表达式)
    /**
     * 数字解释器
     *
     * @author 512573717@qq.com
     * @created 2018/8/14  下午5:16.
     */
    public class NumExpression extends AbstractExpression {
        private int num;
    
        public NumExpression(int num) {
            this.num = num;
        }
    
        @Override
        public int interpret() {
            return num;
        }
    }
    
    3. 操作符的实现(非终结符表达式)
    /**
     * 运算符号抽象解释器
     *
     * @author 512573717@qq.com
     * @created 2018/8/15  上午11:47.
     */
    public class OperationExpression extends AbstractExpression {
        //声明两个成员变量存储运算符号两边的数字解释器
        protected AbstractExpression exp1, exp2;
    
        public OperationExpression(AbstractExpression exp1, AbstractExpression exp2) {
            this.exp1 = exp1;
            this.exp2 = exp2;
        }
    
        @Override
        public int interpret() {
            return 0;
        }
    }
    
    4. 加法运算抽象解释器
    /**
     * 加法运算抽象解释器
     *
     * @author 512573717@qq.com
     * @created 2018/8/15  下午11:10.
     */
    public class AddExpression extends OperationExpression {
        public AddExpression(AbstractExpression exp1, AbstractExpression exp2) {
            super(exp1, exp2);
        }
    
        @Override
        public int interpret() {
            return exp1.interpret() + exp2.interpret();
        }
    }
    
    5. 减法运算抽象解释器
    /**
     * 减法运算抽象解释器
     *
     * @author 512573717@qq.com
     * @created 2018/8/15  下午11:10.
     */
    public class SubExpression extends OperationExpression {
        public SubExpression(AbstractExpression exp1, AbstractExpression exp2) {
            super(exp1, exp2);
        }
    
        @Override
        public int interpret() {
            return exp1.interpret() - exp2.interpret();
        }
    }
    
    6. 处理与解释相关的一些业务
    /**
     * 处理与解释相关的一些业务
     * 
     * @author 512573717@qq.com
     
     * @created 2018/8/15  下午11:12.
     * 
     */
    public class Calculator {
        //声明一个Stack栈存储并操作所有相关的解释器
        private Stack<AbstractExpression> expressions = new Stack<AbstractExpression>();
    
        public Calculator(String expression) {
            //声明两个ArithmeticExpressioin类型的临时变量,存储运算符左右两边的数字解释器
            AbstractExpression exp1, exp2;
    
            String[] elements = expression.split(" ");
    
            for (int i = 0; i < elements.length; i++) {
                switch (elements[i].charAt(0)) {
                    case '+':
                        //将栈中的解释器弹出作为运算符号左边的解释器
                        exp1 = expressions.pop();
                        //同时将运算符号数组角标下一个元素构成
                        exp2 = new NumExpression(Integer.valueOf(elements[++i]));
                        //通过上面两个数字解释器构造加法运算解释器
                        expressions.push(new AddExpression(exp1, exp2));
                        break;
                    case '–':
                        //将栈中的解释器弹出作为运算符号左边的解释器
                        exp1 = expressions.pop();
                        //同时将运算符号数组角标下一个元素构成
                        exp2 = new NumExpression(Integer.valueOf(elements[++i]));
                        //通过上面两个数字解释器构造加法运算解释器
                        expressions.push(new SubExpression(exp1, exp2));
                        break;
                    default://如果为数字
                        /**
                         * 如果不是运算符则数字
                         * 则是数字,直接构造数字解释器并压入栈
                         */
                        expressions.push(new NumExpression(Integer.valueOf(elements[i])));
                        break;
                }
            }
        }
    
        /**
         * 计算
         * @return
         */
        public int calculate() {
            return expressions.pop().interpret();
        }
    
    }
    
    7. Client
        Calculator calculator = new Calculator("1 + 2 + 3 – 4 + 1");
        System.out.println(calculator.calculate());
    

    总结

    优点

      1. 灵活的扩展性,当我们想对文法规则进行扩展时,只需要增加相应的非终结符号解释器,并在构建抽象语法树时,使用到新增的解释器对象进行具体的解释即可,非常方便。

    缺点

       1. 过于复杂的语法,构建其抽象语法树会显得异常繁琐,甚至有可能出现需要构建多棵抽象语法树的情况,因此,对于复杂的文法并不推荐使用解释器模式。
      2. 对于每一条文法都可以对应至少一个解释器,其会生成大量的类,导致后期维护困难。
      3. 由于使用了大量的循环和递归,效率是个不容忽视的问题,特别是用于解析复杂、冗长的语法时,效率是难以忍受的。

    相关文章

      网友评论

          本文标题:设计模式《解释器模式》

          本文链接:https://www.haomeiwen.com/subject/mnmnbftx.html