设计模式之解释器模式

定义

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

结构

  • AbstractExpression,抽象解释器角色,定义解释器的接口,约定解释器的解释操作。
  • TerminalExpression,终结符解释器,用来实现语法规则中和终结符相关的操作,不再包含其他的解释器,类似组合模式中的叶子对象。
  • UonterminalExpression,非终结符解释器,用来实现语法规则中和非终结符相关的操作,可以包含其他的解释器,类似组合模式中的组合对象。
  • Context,上下文,通常包含各个解释器所需要的数据或是公共的功能。

简单实现

抽象解释器

public abstract class AbstractExpression {

  abstract void interpret(Context context);
}

终结符解释器

public class TerminalExpression extends AbstractExpression {

  @Override
  void interpret(Context context) {

  }
}

非终结符解释器

public class NonterminalExpression extends AbstractExpression {

  @Override
  void interpret(Context context) {

  }
}

上下文对象

public class Context {

}

实现一个简单的计算器

支持加减法的简单计算

抽象解释器

import java.util.Map;

public abstract class AbstractExpression {

  abstract int interpreter(Map<String, Integer> context);

}

终结符解释器

import java.util.Map;

public class VarExpression extends AbstractExpression {

  private String key;

  public VarExpression(String key) {
    this.key = key;
  }

  @Override
  public int interpreter(Map<String, Integer> context) {
    if (context.containsKey(key)) {
      return context.get(key);
    }
    throw new IllegalArgumentException("not found field " + key);
  }
}

变量解释器,根据变量名称获取变量值,map作为上下文。

非终结符解释器

public abstract class SymbolExpression extends AbstractExpression {

  protected AbstractExpression left;
  protected AbstractExpression right;

  public SymbolExpression(AbstractExpression left, AbstractExpression right) {

    this.left = left;
    this.right = right;
  }

}

定义一个公共的操作符解释器,每个操作符都要有两个操作数,也就是变量

import java.util.Map;

public class AddExpression extends SymbolExpression {

  public AddExpression(AbstractExpression left, AbstractExpression right) {
    super(left, right);
  }

  //求出 left 和 right  表达式相加后的结果
  public int interpreter(Map<String, Integer> context) {
    return super.left.interpreter(context) + super.right.interpreter(context);
  }
}
import java.util.Map;

public class SubExpression extends SymbolExpression {

  public SubExpression(AbstractExpression left, AbstractExpression right) {
    super(left, right);
  }

  //求出 left 和 right  表达式相减后的结果
  public int interpreter(Map<String, Integer> context) {
    return super.left.interpreter(context) - super.right.interpreter(context);
  }
}

定义加法解释器和减法解释器

计算器

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;

public class Calculator {

  private AbstractExpression expression;

  public Calculator(String expressionString) {
    init(expressionString);
  }

  /**
   * 表达式解析
   */
  private void init(String expressionString) {
    List<Character> charArray = infix2suffix(expressionString);
    Stack<AbstractExpression> stack = new Stack<>();
    for (char item : charArray) {
      if (item == '+') {
        if (stack.size() < 2) {
          throw iae();
        }
        AbstractExpression right = stack.pop();
        AbstractExpression left = stack.pop();
        stack.push(new AddExpression(left, right));
      } else if (item == '-') {
        if (stack.size() < 2) {
          throw iae();
        }
        AbstractExpression right = stack.pop();
        AbstractExpression left = stack.pop();
        stack.push(new SubExpression(left, right));
      } else if (item <= 'z' && item >= 'a') {
        stack.push(new VarExpression(String.valueOf(item)));
      } else {
        throw iae();
      }
    }
    if (stack.size() != 1) {
      throw iae();
    }
    this.expression = stack.pop();
  }

  /**
   * 中缀表达式转后缀表达式
   */
  private List<Character> infix2suffix(String infix) {
    List<Character> res = new ArrayList<>();
    Stack<Character> stack = new Stack<>();
    Set<Character> operatorSet = Set.of('+', '-');
    for (char item : infix.toCharArray()) {
      //是字母,直接添加到结果列表
      if (item <= 'z' && item >= 'a') {
        res.add(item);
        continue;
      }
      if (operatorSet.contains(item)) {
        if (!stack.isEmpty()) {
          res.add(stack.pop());
        }
        stack.push(item);
      } else {
        throw iae();
      }
    }
    while (!stack.isEmpty()) {
      res.add(stack.pop());
    }
    return res;
  }

  private IllegalArgumentException iae() {
    return new IllegalArgumentException("illegal expression");
  }

  public int run(Map<String, Integer> var) {
    return this.expression.interpreter(var);
  }
}

先将 中缀表达式 转成 后缀表达式,如

a+b-c

转成

ab+c-

然后将表达式解析成一个树形结构的解释器

客户端

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class Client {

  public static void main(String[] args) throws IOException {
    String expStr = "a+b-c";

    Map<String, Integer> context = new HashMap<>();
    context.put("a", 12);
    context.put("b", 34);
    context.put("c", 56);

    Calculator calculator = new Calculator(expStr);
    System.out.println("运算结果:" + expStr + "=" + calculator.run(context));
  }

}

输出为

运算结果:a+b-c=-10

解释器模式在Spring中的实现

Spring中的SpelExpressionParser

import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

public class Client {

  public static void main(String[] args) {
    SpelExpressionParser parser = new SpelExpressionParser();
    Expression expression = parser.parseExpression("#a+#b-#c");
    EvaluationContext context = new StandardEvaluationContext();
    context.setVariable("a",12);
    context.setVariable("b",34);
    context.setVariable("c",56);
    System.out.println(expression.getValue(context));//-10
  }

}

表达式解析器SpelExpressionParser根据字符串解析成Expression对象,根据EvaluationContext上下文对象获取执行结果。

各种操作符如加号减号都是SpelNodeImpl对象。

总结

优点

  1. 一个解释器对应一条语法规则的方式,扩展新的语法非常容易。

缺点

  1. 不适合复杂的语法,复杂语法需要构建复杂的抽象语法树。

本质

解释器模式的本质是分离实现,解释执行。将语法规则分离成多个,一个解释器处理一个语法规则。

使用场景

  1. 当一个语言需要解释执行,并且可以将该语言中的句子表示为一个抽象语法树的时候。
  2. 语法规则比较简单

解释器模式在实际开发中应用很少,我们一般不需要自己去构建一种新的语法规则去解释。

参考

大战设计模式【20】—— 解释器模式
设计模式(二十一)——解释器模式(Spring 框架中SpelExpressionParser源码分析)
设计模式的征途—23.解释器(Interpreter)模式
解释器模式(详解版)
研磨设计模式-书籍

posted @ 2021-09-11 16:09  strongmore  阅读(241)  评论(0编辑  收藏  举报