解释器(Interpreter)

定义

给分析对象定义一个语言,并定义该语言的文法表示,再设计一个解析器来解释语言中的句子。也就是说,用编译语言的方式来分析应用中的实例。这种模式实现了文法表达式处理的接口,该接口解释一个特定的上下文。

能解决什么问题

类似正则表达式,是为了解决一类特定,而且重复使用比较多的问题。
和组合模式的区别是,解释器模式侧重于解决文法和元素的组合,并且组成元素多于组合模式(多一个Context类,用来生产解释器)。

优缺点

解释器模式是一种类行为型模式,其主要优点如下:

  1. 扩展性好。由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法。
  2. 容易实现。在语法树中的每个表达式节点类都是相似的,所以实现其文法较为容易。

解释器模式的主要缺点如下:

  1. 执行效率较低。解释器模式中通常使用大量的循环和递归调用,当要解释的句子较复杂时,其运行速度很慢,且代码的调试过程也比较麻烦。
  2. 会引起类膨胀。解释器模式中的每条规则至少需要定义一个类,当包含的文法规则很多时,类的个数将急剧增加,导致系统难以管理与维护。
  3. 可应用的场景比较少。在软件开发中,需要定义语言文法的应用实例非常少,所以这种模式很少被使用到。

特点

解释器模式包含以下主要角色:

  1. 抽象表达式(Abstract Expression)角色:定义解释器的接口,约定解释器的解释操作,主要包含解释方法 interpret()。
  2. 终结符表达式(Terminal Expression)角色:是抽象表达式的子类,用来实现文法中与终结符相关的操作,文法中的每一个终结符都有一个具体终结表达式与之相对应。
  3. 非终结符表达式(Nonterminal Expression)角色:也是抽象表达式的子类,用来实现文法中与非终结符相关的操作,文法中的每条规则都对应于一个非终结符表达式。
  4. 环境(Context)角色:通常包含各个解释器需要的数据或是公共的功能,一般用来传递被所有解释器共享的数据,后面的解释器可以从这里获取这些值。
  5. 客户端(Client):主要任务是将需要分析的句子或表达式转换成使用解释器对象描述的抽象语法树,然后调用解释器的解释方法,当然也可以通过环境角色间接访问解释器的解释方法。

类图

模式23-解释器(Interpreter) - 图1

代码实例

  1. /**
  2. * 抽象解释器
  3. * 用来解释0-9的加减法
  4. */
  5. public interface Expression {
  6. int interpret();
  7. }
  8. /**
  9. * 最终解释器
  10. * 用来把字符串转为数字
  11. */
  12. public class TerminalExpression implements Expression{
  13. private String info;
  14. public TerminalExpression(String info) {
  15. this.info = info;
  16. }
  17. @Override
  18. public int interpret() {
  19. return Integer.parseInt(info);
  20. }
  21. }
  22. /**
  23. * 非终端解释器
  24. * 含有左右2个解释器的
  25. */
  26. public abstract class LeftRightExpression implements Expression{
  27. protected Expression left;
  28. protected Expression right;
  29. // 必须由左右2个解释器构件
  30. public LeftRightExpression(Expression left, Expression right) {
  31. this.left = left;
  32. this.right = right;
  33. }
  34. }
  35. /**
  36. * 非终端解释器
  37. * 加法解释器
  38. */
  39. public class AddExpression extends LeftRightExpression {
  40. public AddExpression(Expression left, Expression right) {
  41. super(left, right);
  42. }
  43. @Override
  44. public int interpret() {
  45. return left.interpret() + right.interpret();
  46. }
  47. }
  48. /**
  49. * 非终端解释器
  50. * 减法解释器
  51. */
  52. public class SubtractExpression extends LeftRightExpression {
  53. public SubtractExpression(Expression left, Expression right) {
  54. super(left, right);
  55. }
  56. @Override
  57. public int interpret() {
  58. return left.interpret() - right.interpret();
  59. }
  60. }
  61. /**
  62. * 环境类
  63. * 用来生成解释器
  64. */
  65. public class Context {
  66. private Expression expression;
  67. /**
  68. * 对字符串运算进行解析
  69. *
  70. * @param info 字符串运算
  71. * @return 计算记过
  72. */
  73. public int calculate(String info) {
  74. this.expression = createExpression(info);
  75. return expression.interpret();
  76. }
  77. /**
  78. * 生成解释器
  79. *
  80. * @param info “1+2-3” 类似这种
  81. * @return 解释器
  82. */
  83. private Expression createExpression(String info) {
  84. Stack<Expression> stack = new Stack<>();
  85. char[] chars = info.toCharArray();
  86. for (int i = 0; i < chars.length; i++) {
  87. char a = chars[i];
  88. switch (a) {
  89. case '+':
  90. Expression left = stack.pop();
  91. Expression right = new TerminalExpression(String.valueOf(chars[++i]));
  92. stack.push(new AddExpression(left, right));
  93. break;
  94. case '-':
  95. Expression left2 = stack.pop();
  96. Expression right2 = new TerminalExpression(String.valueOf(chars[++i]));
  97. stack.push(new SubtractExpression(left2, right2));
  98. break;
  99. case ' ':
  100. break;
  101. default:
  102. stack.push(new TerminalExpression(String.valueOf(a)));
  103. break;
  104. }
  105. }
  106. return stack.pop();
  107. }
  108. }
  109. public class Client {
  110. public static void main(String[] args) {
  111. Context context = new Context();
  112. String info = "1+7+5-3+1";
  113. int calculate = context.calculate(info);
  114. System.out.println(info + "=" + calculate);
  115. }
  116. }
  117. > Task :Client.main()
  118. 1+7+5-3+1=11