一、简述

解释器模式(Interpreter Pattern),实际应用中较少用到的行为模式。主要作用就是提供解释语言的语法或表达式的能力,从作用上来说,注定实际开发过程中会使用的少,毕竟很少有人需要构建一套自己的语法来解析吧!但是,这并不表示解释器模式我们可以忽略掉。

二、实现

解释器模式UML图

解释器模式_ 模式

  • Context:上下文环境,包含解释器之外的全局信息
  • Client:客户端,解析表达式,构建语法树,执行具体的解释操作等
  • AbstractExpression:抽象表达式,声明一个抽象的解释操作类,并定义一个抽象的解释方案,其具体的实现在各个具体的子类解释器中完成。
  • TerminalExpression:终结符表达式,实现文法中终结符有关的解释操作。文法中每一个终结符都有一个具体的终结表达式与之对应。
  • NonterminalExpression:非终结表达式,实现文法中非终结符有关的解释操作。

其中AbstractExpressioninterpret()是抽象的解析方法,参数是上下文的环境,而interpret()方法的具体实现则由TerminalExpressionNonterminalExpression实现。

三、具体实现

如下我们通过对算术表达式的解释来看一个解释器模式的实现, 如表达式m+n+p中,如果我们使用解释器模式对该表达式进行解释,那么mnp代表的三个字母可以看成是终结符号,而+代表的运算符则可以看成是非终结符号。

先创建抽象解释器,表示数学运算

public abstract class ArithmeticExpression {
    public abstract int interptet();
}

解释器中定义了interptet()方法,ArithmeticExpression有两个子类,分别是NumExpressionOperatorExpression

/** 对数字进行解释 */
public class NumExpression extends ArithmeticExpression {
    private int num;
        
    public NumExpression(int num) {
        this.num = num;
    }
        
    @Override 
    public int interptet() {
        return num;
    }
}

/** 对运算符进行解释 */
public abstract class OperatorExpression extends ArithmeticExpression {
    protected ArithmeticExpression mArithmeticExpression1,mArithmeticExpression2;
    
    public OperatorExpression(ArithmeticExpression arithmeticExpression1,ArithmeticExpression arithmeticExpression2) {
        mArithmeticExpression1 = arithmeticExpression1;
        mArithmeticExpression2 = arithmeticExpression2;
    }
}

基础的类已经完成了,如果需要处理加法运算还需要继承OperatorExpression并实现interptet()方法来实现加法运算器AdditionExpression

public class AdditionExpression extends OperatorExpression {
    public AdditionExpression(ArithmeticExpression arithmeticExpression1,ArithmeticExpression arithmeticExpression2) {
        super(arithmeticExpression1, arithmeticExpression2);
    }
    
    @Override
    public int interptet() {
        return mArithmeticExpression1.interptet() + mArithmeticExpression2.interptet();
    }
}

还差一个业务逻辑处理类

public class Calculator {

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

    public Calculator(String expression) {
        //声明两个ArithmeticExpression类型的临时变量,存储运算符左右两边的数字解释器
        ArithmeticExpression arithmeticExpression1, arithmeticExpression2;

        //根据空格分隔表达式字符串
        String[] elements = expression.split(" ");

        //循环遍历表达式元素数组
        for (int i = 0; i < elements.length; ++i) {
            switch (elements[i].charAt(0)) {
                //如果是加号
                case '+':
                    //将栈中的解释器弹出作为运算符号左边的解释器
                    arithmeticExpression1 = mArithmeticExpressionStack.pop();

                    //同时将运算符号数组下标下一个元素构造为一个数字解析器
                    arithmeticExpression2 = new NumExpression(Integer.valueOf(elements[++i]));

                    //通过上面两个数字解释器构造加法运算解释器
                    mArithmeticExpressionStack.push(new AdditionExpression(arithmeticExpression1, arithmeticExpression2));
                    break;
                default:
                    //如果不是运算符,则是数字,直接构造数字解释器并压入栈
                    mArithmeticExpressionStack.push(new NumExpression(Integer.valueOf(elements[i])));
                    break;
            }
        }
    }

    //计算结果
    public int calculate() {
        return mArithmeticExpressionStack.pop().interptet();
    }
}

这里需要注意的是,为了简化逻辑,在约定的表达式的每个元素之间必须使用空格隔开,如123 + 32 + 666这种形式的表达式,这样才能在Calculator中使用空格来拆分字符串。

如果想引入减法运算,我们只需要定义一个减法解释器

public class SubtractionExpreesion extends OperatorExpression{
    public SubtractionExpreesion(ArithmeticExpression arithmeticExpression1,ArithmeticExpression arithmeticExpression2) {
        super(arithmeticExpression1, arithmeticExpression2);
    }
    
    @Override
    public int interptet() {
        return mArithmeticExpression1.interptet() - mArithmeticExpression2.interptet();
    }
}

Calculatorswitch中添加如下代码即可

case "-":
    arithmeticExpression1 = mArithmeticExpressionStack.pop();
    arithmeticExpression2 = new NumExpression(Integer.valueOf(elements[++i]));
    
    mArithmeticExpressionStack.push(new SubtractionExpreesion(arithmeticExpression1, arithmeticExpression2));
    break;

四、小结

这里,我们能看出来解释器模式灵活性强,但是这是对于相对简单的语言;如果需要加入乘除取余等等,一并进行混合预算的话还需要考虑不同符号的运算优先级逻辑处理,所以在“简单的语言”中适用解释器模式。其实解释器模式的本质就是,将复杂的问题模块化,分离实现、解释执行。

________________________________________________________

作者:MrTrying
链接:https://www.jianshu.com/p/0e7e26bbe204
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。