设计模式系列-第二十四章(行为型-解释器模式)

本文介绍了解释器模式,用于定义语言的文法并创建解释器来处理这种语言。以一个加减乘除计算语言为例,详细展示了如何实现解释器模式,包括普通实现和更灵活的解释器模式实现。通过将解析逻辑拆分为多个小类,降低了代码复杂性,便于维护。
摘要由CSDN通过智能技术生成

一、解释器模式

    解释器模式为某个语言定义它的语法(或者叫文法)表示,并定义一个解释器用来处理这个语法。 它用来描述如何构建一个简单的“语言”解释器。解释器模式很小众,只在一些特定的领域会被用到,比如编译器、规则引擎、正则表达式。

二、解释器模式的实现

假设我们定义了一个新的加减乘除计算“语言”,语法规则如下:

  • 运算符只包含加、减、乘、除,并且没有优先级的概念;
  • 表达式(也就是前面提到的“句子”)中,先书写数字,后书写运算符,空格隔开;
  • 按照先后顺序,取出两个数字和一个运算符计算结果,结果重新放入数字的最头部位置,循环上述过程,直到只剩下一个数字,这个数字就是表达式最终的计算结果。

    比如“ 8 3 2 4 - + * ”这样一个表达式,我们按照上面的语法规则来处理,取出数字“8 3”和“-”运算符,计算得到 5,于是表达式就变成了“ 5 2 4 + * ”。然后,我们再取出“ 5 2 ”和“ + ”运算符,计算得到 7,表达式就变成了“ 7 4 * ”。最后,我们取出“ 7 4”和“ * ”运算符,最终得到的结果就是 28。

 2.1 普通的实现

用户按照上面的规则书写表达式,传递给 interpret() 函数,就可以得到最终的计算结果。


public class ExpressionInterpreter {
  private Deque<Long> numbers = new LinkedList<>();

  public long interpret(String expression) {
    String[] elements = expression.split(" ");
    int length = elements.length;
    for (int i = 0; i < (length+1)/2; ++i) {
      numbers.addLast(Long.parseLong(elements[i]));
    }

    for (int i = (length+1)/2; i < length; ++i) {
      String operator = elements[i];
      boolean isValid = "+".equals(operator) || "-".equals(operator)
              || "*".equals(operator) || "/".equals(operator);
      if (!isValid) {
        throw new RuntimeException("Expression is invalid: " + expression);
      }

      long number1 = numbers.pollFirst();
      long number2 = numbers.pollFirst();
      long result = 0;
      if (operator.equals("+")) {
        result = number1 + number2;
      } else if (operator.equals("-")) {
        result = number1 - number2;
      } else if (operator.equals("*")) {
        result = number1 * number2;
      } else if (operator.equals("/")) {
        result = number1 / number2;
      }
      numbers.addFirst(result);
    }

    if (numbers.size() != 1) {
      throw new RuntimeException("Expression is invalid: " + expression);
    }

    return numbers.pop();
  }
}

 2.2 解释器模式的实现 

    在上面2.1的代码实现中,语法规则的解析逻辑(第 23、25、27、29 行)都集中在一个函数中,对于简单的语法规则的解析,这样的设计就足够了。但是,对于复杂的语法规则的解析,逻辑复杂,代码量多,所有的解析逻辑都耦合在一个函数中,这样显然是不合适的。这个时候,我们就要考虑拆分代码,将解析逻辑拆分到独立的小类中。该怎么拆分呢?我们可以借助解释器模式。 

    解释器模式的代码实现比较灵活,没有固定的模板。我们前面也说过,应用设计模式主要是应对代码的复杂性,实际上,解释器模式也不例外。它的代码实现的核心思想,就是将语法解析的工作拆分到各个小类中,以此来避免大而全的解析类。一般的做法是,将语法规则拆分成一些小的独立的单元,然后对每个单元进行解析,最终合并为对整个语法规则的解析。前面定义的语法规则有两类表达式,一类是数字,一类是运算符,运算符又包括加减乘除。

    利用解释器模式,我们把解析的工作拆分到 NumberExpression、AdditionExpression、SubstractionExpression、MultiplicationExpression、DivisionExpression 这样五个解析类中。按照这个思路,我们对代码进行重构,重构之后的代码如下所示。当然,因为加减乘除表达式的解析比较简单,利用解释器模式的设计思路,看起来有点过度设计。不过呢,这里我主要是为了解释原理,你明白意思就好,不用过度细究这个例子。


public interface Expression {
  long interpret();
}

public class NumberExpression implements Expression {
  private long number;

  public NumberExpression(long number) {
    this.number = number;
  }

  public NumberExpression(String number) {
    this.number = Long.parseLong(number);
  }

  @Override
  public long interpret() {
    return this.number;
  }
}

public class AdditionExpression implements Expression {
  private Expression exp1;
  private Expression exp2;

  public AdditionExpression(Expression exp1, Expression exp2) {
    this.exp1 = exp1;
    this.exp2 = exp2;
  }

  @Override
  public long interpret() {
    return exp1.interpret() + exp2.interpret();
  }
}
// SubstractionExpression/MultiplicationExpression/DivisionExpression与AdditionExpression代码结构类似,这里就省略了

public class ExpressionInterpreter {
  private Deque<Expression> numbers = new LinkedList<>();

  public long interpret(String expression) {
    String[] elements = expression.split(" ");
    int length = elements.length;
    for (int i = 0; i < (length+1)/2; ++i) {
      numbers.addLast(new NumberExpression(elements[i]));
    }

    for (int i = (length+1)/2; i < length; ++i) {
      String operator = elements[i];
      boolean isValid = "+".equals(operator) || "-".equals(operator)
              || "*".equals(operator) || "/".equals(operator);
      if (!isValid) {
        throw new RuntimeException("Expression is invalid: " + expression);
      }

      Expression exp1 = numbers.pollFirst();
      Expression exp2 = numbers.pollFirst();
      Expression combinedExp = null;
      if (operator.equals("+")) {
        combinedExp = new AdditionExpression(exp1, exp2);
      } else if (operator.equals("-")) {
        combinedExp = new AdditionExpression(exp1, exp2);
      } else if (operator.equals("*")) {
        combinedExp = new AdditionExpression(exp1, exp2);
      } else if (operator.equals("/")) {
        combinedExp = new AdditionExpression(exp1, exp2);
      }
      long result = combinedExp.interpret();
      numbers.addFirst(new NumberExpression(result));
    }

    if (numbers.size() != 1) {
      throw new RuntimeException("Expression is invalid: " + expression);
    }

    return numbers.pop().interpret();
  }
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值