阅读 95

6月更文挑战|设计模式 —— 解释器模式

这是我参与更文挑战的第10天,活动详情查看:更文挑战

解释器模式

解释器模式是一种行为型模式,提供一种解释语言的语法和表达式的方法,定义一个表达式接口,通过接口解释一个特定上下文。但实际上使用解释器模式的场景并不多,相对来说也不太常遇到。不过既然是设计模式中的一种,那必然也有它可用的独特模式。

给定一个语言,定义它的文法一种表示,并定义一个解释器,则解释器使用该表示来解释语言中的句子。

实现

解释器常见场景是对算术表达式的解释。比如表达式m + n + p,如果我们使用解释器模式对表达式进行解释,表示数字的m/n/p可以作为终结符号,”+“可以当做非终结符号。

ArithmeticExpression抽象类定义一个interpret方法。NumExpression用来对数字进行解释,主要目的是获取数字。OperatorExpression是抽象类,声明了两个ArithmeticExpression用于存储NumExpression运算符两边的数字解释器。AdditionExpression继承OperatorExpression实现了加法运算解释器的作用。

实现了文法中对每个结构做解析的解释器之后,再创建一个Calculator类去处理逻辑。输入整个文法内容:"153 + 896 + 789",通过分割将内容不断分割解析成每个对应解释器,最后将解释器组装输出最终结果。

public abstract class ArithmeticExpression {
    /**
     * 抽象的解析方法
     * 具体的解析逻辑由具体的子类实现
     *
     * @return 解析得到具体的值
     */
    public abstract int interpret();
}

public class NumExpression extends ArithmeticExpression {

    private int num;

    public NumExpression(int num) {
        this.num = num;
    }

    @Override
    public int interpret() {
        return num;
    }
}
public abstract class OperatorExpression extends ArithmeticExpression {

    public ArithmeticExpression exp1;
    public ArithmeticExpression exp2;

    public OperatorExpression(ArithmeticExpression exp1, ArithmeticExpression exp2) {
        this.exp1 = exp1;
        this.exp2 = exp2;
    }
}

public class AdditionExpression extends OperatorExpression {

    public AdditionExpression(ArithmeticExpression exp1, ArithmeticExpression exp2) {
        super(exp1, exp2);
    }

    @Override
    public int interpret() {
        return exp1.interpret() + exp2.interpret();
    }
}



public class Calculator {

    // 声明一个Stack栈存储并操作所有相关解释器
    private Stack<ArithmeticExpression> mExpStack = new Stack<>();

    public Calculator(String expression) {
        // 声明两个ArithmeticExpression类型的临时变量,存储运算符左右两边的数字解释器
        ArithmeticExpression exp1;
        ArithmeticExpression exp2;
        // 根据空格分割表达式字符串
        String[] elements = expression.split(" ");

        /**
         * 循环遍历表达式字符串
         */
        for (int i = 0; i < elements.length; i++) {
            /**
             * 判断运算符号
             */
            switch (elements[i].charAt(0)) {
                case '+': // 如果是加号
                    // 则将栈中的解释器弹出作为运算符号左边的解释器
                    exp1 = mExpStack.pop();
                    // 同时将运算符号数组下标下一个元素构造为一个数字解析器
                    exp2 = new NumExpression(Integer.valueOf(elements[++i]));
                    // 通过上面两个数字解释器构造加法运算解释器
                    mExpStack.push(new AdditionExpression(exp1, exp2));
                    break;
                default: // 如果为数字
                    /**
                     * 如果不是运算符号则为数字
                     * 若是数字,直接构造数字解释器并压入栈
                     */
                    mExpStack.push(new NumExpression(Integer.valueOf(elements[i])));
                    break;
            }
        }
    }

    public int calculate() {
        return mExpStack.pop().interpret();
    }
}

Calculator calculator = new Calculator("153 + 896 + 789");
calculator.calculate();
复制代码

总结

  1. 解释器模式优点:灵活扩展性,当对文法规则进行扩展延伸时,只需要添加相应非终结符解释器,在构建抽象语法树时使用新增解释器对象进行解释即可。
  2. 解释器模式优点:对于每一条文法要至少对应一个解释器,类数量庞大,后期维护困难。如果文法是复杂抽象的,那么构建使用解释器模式并不非常合适的选择。

参考

  • 《Android源码设计模式》
文章分类
阅读
文章标签