• Java核心篇,二十三种设计模式(十五),行为型——解析器模式


    一、概述

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

    语言:指的是使用规定格式和文法的一类字符组合

    文法:简单点说就是我们俗称的“语法规则”

    解释器模式的本质:分离实现,解释执行

    二、适用场景

    当有一个语言需要解释执行,并且你可将该语言中的句子表示为一个抽象语法树时,可使 用解释器模式。而当存在以下情况时该模式效果最好:

    • 该文法简单对于复杂的文法,文法的类层次变得庞大而无法管理
    • 效率不是一个关键问题最高效的解释器通常不是通过直接解释语法分析树实现的,而是首先将它们转换成另一种形式

    三、优缺点

    优点

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

    缺点

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

    四、成员角色

    • AbstractExpression(抽象表达式) ===> 声明一个抽象的解释操作,这个接口为抽象语法树中所有的节点所共享
    • TerminalExpression(终结符表达式) ===> 是抽象表达式的子类,用来实现文法中与终结符相关的操作,文法中的每一个终结符都有一个具体终结表达式与之相对
    • NonterminalExpression(非终结符表达式) ===> 是抽象表达式的子类,用来实现文法中与非终结符相关的操作,文法中的每条规则都对应于一个非终结符表达式
    • Context(上下文) ===> 通常包含各个解释器需要的数据或是公共的功能,一般用来传递被所有解释器共享的数据,后面的解释器可以从这里获取这些值
    • Client(客户端) ===> 主要任务是将需要分析的句子或表达式转换成使用解释器对象描述的抽象语法树,然后调用解释器的解释方法,当然也可以通过环境角色间接访问解释器的解释方法

    五、业务分析

    假设我们输入的值是一个仅支持加减法的字符串,我们需要计算出该串的计算结果 a + b +c +d +e + f;而变量 a-f 对应的值通过一个 hashmap 给出

    六、代码实现

    终结符与非终结符的划分依据:

    终结符表达式(TerminalExpression):实现与文法中的元素相关联的解释操作,通常一个解释器模式中只有一个终结符表达式,但有多个实例,对应不同的终结符。

    终结符一半是文法中的运算单元,比如有一个简单的公式R=R1+R2,在里面R1和R2就是终结符,对应的解析R1和R2的解释器就是终结符表达式

    在算术运算中,终结符表达式一般代表数字,因为数字没有办法再分,没有子类,所以是终结符


    非终结符表达式(NonterminalExpression):文法中的每条规则对应于一个非终结符表达式,非终结符表达式一般是文法中的运算符或者其他关键字,比如公式R=R1+R2中,+ 就是非终结符,解析+的解释器就是一个非终结符表达式

    非终结符表达式根据逻辑的复杂程度而增加,原则上每个文法规则都对应一个非终结符表达式

    在算术运算中,非终结表达式一般表示运算符,因为运算符需要操作数,不能单独操作,所以各种运算符又代表加减乘除表达式,因此是非终结式的

    AbstractExpression

    1. package com.designer.interpreter.AbstractExpression;
    2. import java.util.HashMap;
    3. /**
    4. * 抽象表达式,声明解释操作
    5. */
    6. public abstract class AbstractExpression {
    7. public abstract int interprete(HashMap<String, Integer> values);
    8. }

    SymbolExpression

    1. package com.designer.interpreter.NonterminalExpression;
    2. import com.designer.interpreter.AbstractExpression.AbstractExpression;
    3. /**
    4. * 非终结符表达式,运算符(此处为加法和减法)的抽象父类,真正的解释操作由其子类来实现
    5. */
    6. public abstract class SymbolExpression extends AbstractExpression {
    7. protected AbstractExpression left;
    8. protected AbstractExpression right;
    9. // 非终结符表达式的解释操作只关心自己左右两个表达式的结果
    10. public SymbolExpression(AbstractExpression left, AbstractExpression right) {
    11. this.left = left;
    12. this.right = right;
    13. }
    14. }

    AddExpression

    1. package com.designer.interpreter.NonterminalExpression;
    2. import com.designer.interpreter.AbstractExpression.AbstractExpression;
    3. import java.util.HashMap;
    4. /**
    5. * NonterminalExpression(非终结符表达式)
    6. * 加法表达式
    7. */
    8. public class AddExpression extends SymbolExpression{
    9. public AddExpression(AbstractExpression left, AbstractExpression right) {
    10. super(left, right);
    11. }
    12. @Override
    13. public int interprete(HashMap<String, Integer> values) {
    14. return super.left.interprete(values) + super.right.interprete(values);
    15. }
    16. }

    SubExpression

    1. package com.designer.interpreter.NonterminalExpression;
    2. import com.designer.interpreter.AbstractExpression.AbstractExpression;
    3. import java.util.HashMap;
    4. /**
    5. * NonterminalExpression(非终结符表达式)
    6. * 减法表达式
    7. */
    8. public class SubExpression extends SymbolExpression {
    9. public SubExpression(AbstractExpression left, AbstractExpression right) {
    10. super(left, right);
    11. }
    12. // 左右两个表达式相减
    13. public int interprete(HashMap<String, Integer> values) {
    14. return super.left.interprete(values) - super.right.interprete(values);
    15. }
    16. }

    VarExpression

    1. package com.designer.interpreter.TerminalExpression;
    2. import com.designer.interpreter.AbstractExpression.AbstractExpression;
    3. import java.util.HashMap;
    4. /**
    5. * 终结符表达式,代表参加运算的元素对象
    6. */
    7. public class VarExpression extends AbstractExpression {
    8. private String key;
    9. public VarExpression(String key) {
    10. this.key = key;
    11. }
    12. //
    13. public int interprete(HashMap<String, Integer> values) {
    14. return (Integer) values.get(this.key);
    15. }
    16. }

    Calculator

    1. package com.designer.interpreter.Context;
    2. import com.designer.interpreter.AbstractExpression.AbstractExpression;
    3. import com.designer.interpreter.NonterminalExpression.AddExpression;
    4. import com.designer.interpreter.NonterminalExpression.SubExpression;
    5. import com.designer.interpreter.TerminalExpression.VarExpression;
    6. import java.util.HashMap;
    7. import java.util.Stack;
    8. /**
    9. * Context(上下文)
    10. */
    11. public class Calculator {
    12. private AbstractExpression expression;
    13. /**
    14. * 对公式进行解析操作
    15. * 输入的公式
    16. */
    17. public Calculator(String expStr) {
    18. // 定义一个堆栈,安排运算的先后顺序
    19. Stack<AbstractExpression> stack = new Stack<>();
    20. // 表达式拆分为字符数组
    21. char[] charArray = expStr.toCharArray();
    22. // 运算
    23. AbstractExpression left = null;
    24. AbstractExpression right = null;
    25. for (int i = 0; i < charArray.length; i++) {
    26. switch (charArray[i]) {
    27. case '+': // 加法
    28. left = stack.pop();
    29. right = new VarExpression(String.valueOf(charArray[++i]));
    30. stack.push(new AddExpression(left, right));
    31. break;
    32. case '-': // 减法
    33. left = stack.pop();
    34. right = new VarExpression(String.valueOf(charArray[++i]));
    35. stack.push(new SubExpression(left, right));
    36. break;
    37. default: // 公式中的变量
    38. stack.push(new VarExpression(String.valueOf(charArray[i])));
    39. }
    40. }
    41. // 把运算结果抛出来
    42. this.expression = stack.pop();
    43. }
    44. // 计算结果
    45. public int calculate(HashMap<String, Integer> var) {
    46. return this.expression.interprete(var);
    47. }
    48. }

    Test

    1. package com.designer.interpreter;
    2. import com.designer.interpreter.Context.Calculator;
    3. import java.util.HashMap;
    4. public class Test {
    5. public static void main(String[] args) {
    6. HashMap<String, Integer> values = new HashMap<String, Integer>();
    7. values.put("a", 10);
    8. values.put("b", 20);
    9. values.put("c", 30);
    10. values.put("d", 40);
    11. values.put("e", 50);
    12. values.put("f", 60);
    13. Calculator calc = new Calculator("a+b+c+d+e+f");
    14. int result = calc.calculate(values);
    15. System.out.println("a+b+c+d+e+f=" + result);
    16. }
    17. }

  • 相关阅读:
    Spring 对于事务上的应用的详细说明
    谷歌翻译下载-大家都在用的批量谷歌翻译软件下载
    教你手撕MybatisPlus分页原理
    贪吃蛇(C语言实现)
    直播带货系统,乡村直播电商平台的新选择
    某讯D-Link AC集中管理平台未授权访问漏洞复现 CNVD-2023-19479
    律师事务所站
    实例讲解数据库的数据去重
    谈谈你对Promise的理解
    ubuntu 将youtube视频搬运到B站
  • 原文地址:https://blog.csdn.net/ITlanyue/article/details/125469555