美文网首页
Android 解释器模式

Android 解释器模式

作者: yangMr | 来源:发表于2017-12-23 16:07 被阅读0次

    源码地址

    介绍

    解释器模式是一种使用的比较少的一种模式,其提供了一种解释语言的语法或表达式的方式,该模式定义了一个表达式接口,通过该接口解释一个特定的上下文。(比较抽象,想了解建议查看原版书籍)

    使用场景

    1. 如果某个简单的语言需要解释执行,而且可以将该语言中的语句表示为一个抽象语法树时,可以考虑使用该模式。比如有一个简单的数学表达式:p+q+m-n。
    2. 在某些特定领域出现不断重复的问题时,可以将该领域的问题转换为一种语法规则下的语句,然后构建解释器来解释该语句。比如需要将一段阿拉伯数字转换为中文的数字,又或者将某个小写英文短语转换为大写。

    UML 类图

    解释器模式UML.jpg

    角色介绍。

    AbstractExpression: 抽象表达式。
    声明一个抽象的解释操作父类,并定义一个抽象的解释方法,其具体的实现在各个具体的子类解释器中完成。

    TerminalExpression: 终结符表达式。
    实现文法与终结符有关的解释操作。文法中每一个终结符都有一个具体的终结表达式与之对应。

    NonternimalExpression: 非终结符表达式。
    实现文法中与非终结符有关的解释操作。

    Context: 上下文环境类。
    包含解释器之外的全局信息。

    Client: 客户类。
    解析表达式,构建抽象语法树,执行具体的解释操作等。

    对应通用模式代码

    // 抽象表达式
    public abstract class AbstractExpression {
        /**
         * 抽象的解析方法
         */
        public abstract void interpret(Context ctx);
    }
    
    // 终结符表达式
    public class TerminalExpression extends AbstractExpression {
        @Override
        public void interpret(Context ctx) {
            //实现文法中与终结符有关的解释操作
        }
    }
    
    // 非终结符表达式
    public class NonterminalExpression extends AbstractExpression {
        @Override
        public void interpret(Context ctx) {
            //实现文法中与非终结符有关的解释操作
        }
    }
    
    // 上下文环境类,包含解释器之外的全局信息
    public class Context {
    }
    
    // 客户类
    public class Client {
        public static void main(String[] args) {
            //根据文法对特定句子构建抽象语法树后解释
        }
    }
    

    简单实现

    需求:比如算数表达式 m + n + p。代表数字的m、n、p三个字符看成终结符号,+ 看做非终结符号。

    1. 抽象的算术运算解释器,为所有解释器共性的提取

      public abstract class ArithmeticExpression {
          /**
           * 抽象的解析方法
           * 具体的解析逻辑由具体的子类实现
           * @return 解析得到具体的值
           */
          public abstract int interpret();
      }
      
    2. 数字解释器,仅仅为了解释数字

      public class NumExpression extends ArithmeticExpression{
          private int num;
      
          public NumExpression(int num) {
              this.num = num;
          }
      
          @Override
          public int interpret() {
              return num;
          }
      }
      
    3. 运算符号抽象解释器,为所有运算符号解释器共性的提取

      public abstract class OperatorExpression extends ArithmeticExpression {
          //声明两个成员变量存储运算符号两边的数字解释器
          protected ArithmeticExpression exp1, exp2;
      
          public OperatorExpression(ArithmeticExpression exp1, ArithmeticExpression exp2) {
              this.exp1 = exp1;
              this.exp2 = exp2;
          }
      }
      
    4. 加法运算抽象解释器

      public class AdditionExpression extends OperatorExpression {
          public AdditionExpression(ArithmeticExpression exp1, ArithmeticExpression exp2) {
              super(exp1, exp2);
          }
      
          @Override
          public int interpret() {
              return exp1.interpret() + exp2.interpret();
          }
      }
      
    5. 处理与解释相关的一些业务

      public class Calculator {
          //声明一个Stack栈存储并操作所有相关的解释器
          private Stack<ArithmeticExpression> mExpStack = new Stack<>();
      
          public Calculator(String expression) {
              //声明两个TerminalExpression类型的临时变量,存储运算符左右两边的数字解释器
              ArithmeticExpression exp1, 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();
          }
      }
      
    6. 客户类

      public class Client {
          public static void main(String[] args) {
              Calculator calculator = new Calculator("1 + 2 + 3 + 10");
              System.out.println(calculator.calculate());
          }
      }
      

    此时只是定义了加法运算,如果需要增加减法运算,则可以在 Calculator 中增加以下分支

    case '-':   //如果是减号
        exp1 = mExpStack.pop();
        exp2 = new NumExpression(Integer.valueOf(elements[++i]));
        mExpStack.push(new SubtractionExpression(exp1, exp2));
        break;
    

    此时,在 Client 中就可以开始使用了

    public class Client {
        public static void main(String[] args) {
            Calculator calculator = new Calculator("1 - 2 - 3 + 10");
            System.out.println(calculator.calculate());
        }
    }
    

    Android 源码中的实现

    AndroidManifest.xml 配置文件的读取。

    总结

    1. 优点

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

    2. 缺点

      每一条文法都可以对应至少一个解释器,其会生成大量的类,导致后期维护困难;同时,对于复杂的文法,构建其抽象语法树会显得异常繁琐,甚至有可能会出现需要构建多棵抽象语法树的情况,因此,对于复杂的文法并不推荐使用解释器模式。

    相关文章

      网友评论

          本文标题:Android 解释器模式

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