基本需求

  • 实现四则运算,如计算a+b-c+d的值

    • 先输入表达式的形式,如a+b-c+d,要求表达式正确
    • 再分别输出a,b,c,d的值
    • 最后求出结果

传统方案

  • 编写一个方法,接收表达式的形式,根据用户输入的数值进行解析,得到结果
  • 如果加入新的运算符,比如*/(等等,不利于扩展,另外让一个方法解析会造成程序结构的混乱
  • 使用解释器模式,表达式 -> 解释器(多种) -> 结果

基本介绍

  • 在编译原理中,一个算术表达式通过词法分析器形成词法单元,而后这些词法单元再通过语法分析器构建语法分析树,最终形成一颗抽象的语法分析树,这里的词法分析器和语法分析器都可以看做是解释器

  • 解释器模式(Interpreter)提供了评估语言的语法或表达式的方式,它属于行为型模式。这种模式实现了一个表达式接口,该接口解释一个特定的上下文。这种模式被用在SQL解析、符号处理引擎等

  • 解释器模式指给定一个语言(表达式),定义它的文法的一种表示,并定义一个解释器,使用该解释器来解释语言中的句子(表达式)

  • 应用场景

    • 应用可以将一个需要解释执行的语言中的句子表示为一个抽象语法树
    • 一些重复出现的问题可以用一种简单的语言来表达
    • 一个简单语法需要解释的场景
    • 比如编译器、运算表达式计算、正则表达式等
  • UML类图(原理)

    • 说明

      • Context:是环境角色,含有解释器之外的全局信息
      • AbstractExpression:抽象表达式,声明一个抽象的解释器操作,这个方法为抽象语法树中所有的节点所共享
      • TerminalExpression:终结符表达式,实现与文法中的终结符相关的解释操作(相当于递归的出口)
      • NonTerminalExpression:非终结符表达式,为文法中的非终结符实现解释操作(需要聚合AbstractExpression,可能是TerminalExpression 或 NonTerminalExpression,最终构成一个递归结构)
      • Context 和 TerminalExpression 信息通过Client输入即可
  • UML类图(案例)

  • 构建解释器流程示意

  • 代码实现

    • // 抽象表达式类
      public abstract class AbstractExpression { // 声明一个解释操作 map的含义是作为终结者表达式获取值的方式 一般有Context提供,也就是递归的出口
      // 本次案例中 map的形式为{a=1,b=2,c=3...} key 为 表达式中字母 value为每个字母具体的值
      public abstract int interpreter (Map<String, Integer> map); }
    • // 变量表达式 此处作为了终结表达式 提供递归的出口
      public class VarExpression extends AbstractExpression { // 此处为key为表达式的字母
      private String key; public VarExpression(String key) {
      this.key = key;
      } @Override
      public int interpreter(Map<String, Integer> map) {
      // 根据Context提供的map 根据key 获取对应的值 作为递归的出口 例如 map.get("a") = 1
      return map.get(this.key);
      }
      }
    • // 符号表达式 为非终结表达式 此处仍然作为一个抽象类 因为符号有多种
      public abstract class SymbolExpression extends AbstractExpression{ // 聚合AbstractExpression 作为左表达式和右表示式 均为AbstractExpression下的某一子类
      // 左右表达式可能是终结表达式也可能是非终结表达式
      // 如果是终结表达式 作为递归的出口 直接在map中获取值,如果是非终结表达式 将map向下传递进行递归 直至递归出口
      protected AbstractExpression left; protected AbstractExpression right; public SymbolExpression(AbstractExpression left, AbstractExpression right) {
      this.left = left;
      this.right = right;
      }
      } // 子类一 减法表达式
      class SubExpression extends SymbolExpression { public SubExpression(AbstractExpression left, AbstractExpression right) {
      super(left, right);
      } // 实现减法操作 左表达式的值 - 右表达式的值
      @Override
      public int interpreter(Map<String, Integer> map) {
      return this.left.interpreter(map) - this.right.interpreter(map);
      }
      } // 子类二 加法表达式
      class AddExpression extends SymbolExpression { public AddExpression(AbstractExpression left, AbstractExpression right) {
      super(left, right);
      } // 实现加法操作 左表达式的值 + 右表达式的值
      @Override
      public int interpreter(Map<String, Integer> map) {
      return this.left.interpreter(map) + this.right.interpreter(map);
      }
      } // 如需*/等 直接加子类即可
    • // 计算器类 此处作为Context环境类 向解释器提供环境
      public class Calculator { private AbstractExpression abstractExpression; public Calculator(String expression) {
      // 使用栈 构建解释器 递归形式 始终保持栈中只会有一个元素(但嵌套了很多AbstractExpression)
      Stack<AbstractExpression> abstractExpressionStack = new Stack<>();
      char[] chars = expression.toCharArray();
      AbstractExpression left = null;
      AbstractExpression right = null;
      for (int i = 0; i < chars.length; i++) {
      switch (chars[i]) {
      case '+' :
      // 是加法 在栈中取出左表达式 创建右表达式 构建AddExpression入栈
      left = abstractExpressionStack.pop();
      right = new VarExpression(String.valueOf(chars[++i]));
      abstractExpressionStack.push(new AddExpression(left, right));
      break;
      case '-' :
      // 是减法 在栈中取出左表达式 创建右表达式 构建SubExpression入栈
      left = abstractExpressionStack.pop();
      right = new VarExpression(String.valueOf(chars[++i]));
      abstractExpressionStack.push(new SubExpression(left, right));
      break;
      default:
      // 是字母 直接构建VarExpression 并入栈 作为递归的出口
      abstractExpressionStack.push( new VarExpression(String.valueOf(chars[i])));
      break;
      }
      }
      // 在栈中取出构建好的表达式并赋值给成员变量
      this.abstractExpression = abstractExpressionStack.pop();
      } // 使用Context中的环境进行解释(也就是计算表达式的值)
      public int run(Map<String, Integer> map) {
      return this.abstractExpression.interpreter(map);
      }
      }
    • public class Client {
      public static void main(String[] args) {
      System.out.println("请输入表达式:");
      Scanner scanner = new Scanner(System.in);
      String expr = scanner.nextLine();
      // 表达式的数组
      char[] chars = expr.toCharArray();
      // 创建Map用于给解释器提供参数
      Map<String, Integer> map = new HashMap<>();
      for (char aChar : chars) {
      String value;
      if ('+' != aChar && '-' != aChar) {
      if (!map.containsKey(String.valueOf(aChar))) {
      System.out.println("请输入" + aChar + "的值:");
      value = scanner.nextLine();
      map.put(String.valueOf(aChar), Integer.valueOf(value));
      }
      }
      }
      // 使用计算器 进行计算 将构建好的map通过Context传递给解释器
      Calculator calculator = new Calculator(expr);
      System.out.println("表达式" + expr + "的计算结果是:" + calculator.run(map));
      }
      }

spring源码

  • 在spring中的SpelExpressionParse中就使用到了解释器模式

  • 测试代码

    • public class SpelExpressionParseTest {
      public static void main(String[] args) {
      SpelExpressionParser spelExpressionParser = new SpelExpressionParser();
      Expression expression = spelExpressionParser.parseExpression("(5 + 5) * 6");
      Integer value = (Integer) expression.getValue();
      System.out.println(value);
      }
      }
  • UML类图

    • 说明

      • TemplateAwareExpressionParser为ExpressionParser接口的实现类,实现了parseExpression方法,该方法中调用了parseTemplate方法和doParseExpression方法
      • parseTemplate方法根据参数返回不同的解释器LiteralExpression和CompositeStringExpression
      • 而doParseExpression方法为抽象方法,其实现在子类SpelParseExpression和InternalSpelExpressionParse中,其返回值都是SpelExpression
      • Expression接口为解释器接口,其中有三个实现类,其中的getValue方法为解释方法

注意事项

  • 当有一个语言需要解释执行,可将该语言中的句子表示为一个抽象语法树,就可以考虑使用解释器模式,让程序具有良好的扩展性
  • 使用解释器可能会引起类膨胀、解释器模式采用递归调用方法,将会导致调试非常复杂、效率可能会降低

20.java设计模式之解释器模式的更多相关文章

  1. 折腾Java设计模式之解释器模式

    解释器模式 解释器模式是类的行为模式.给定一个语言之后,解释器模式可以定义出其文法的一种表示,并同时提供一个解释器.客户端可以使用这个解释器来解释这个语言中的句子. 意图 给定一个语言,定义它的文法表 ...

  2. JAVA设计模式之解释器模式

    在阎宏博士的<JAVA与模式>一书中开头是这样描述解释器(Interpreter)模式的: 解释器模式是类的行为模式.给定一个语言之后,解释器模式可以定义出其文法的一种表示,并同时提供一个 ...

  3. 简单的介绍一下Java设计模式:解释器模式

    目录 定义 意图 主要解决问题 优缺点 结构 示例 适用情况 定义 解释器模式是类的行为型模式,给定一个语言之后,解释器模式可以定义出其文法的一种表示,并同时提供一个解释器,客户端可以使用这个解释器来 ...

  4. java设计模式5--原型模式(Prototype)

    本文地址:http://www.cnblogs.com/archimedes/p/java-prototype-pattern.html,转载请注明源地址. 原型模式 用原型实例指定创建对象的种类,并 ...

  5. 【设计模式】Java设计模式 - 装饰者模式

    Java设计模式 - 装饰者模式 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 原创作品,更多关注我CSDN: 一个有梦有戏的人 准备将博客园.CSDN一起记录分享自 ...

  6. Java设计模式——装饰者模式

    JAVA 设计模式 装饰者模式 用途 装饰者模式 (Decorator) 动态地给一个对象添加一些额外的职责.就增加功能来说,Decorator 模式相比生成子类更为灵活. 装饰者模式是一种结构式模式 ...

  7. 浅析JAVA设计模式之工厂模式(一)

    1 工厂模式简单介绍 工厂模式的定义:简单地说,用来实例化对象,取代new操作. 工厂模式专门负责将大量有共同接口的类实例化.工作模式能够动态决定将哪一个类实例化.不用先知道每次要实例化哪一个类. 工 ...

  8. 乐在其中设计模式(C#) - 解释器模式(Interpreter Pattern)

    原文:乐在其中设计模式(C#) - 解释器模式(Interpreter Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 解释器模式(Interpreter Pattern) 作 ...

  9. JAVA设计模式--装饰器模式

    装饰器模式 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构.这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装. 这种模式创建了一个装饰 ...

随机推荐

  1. wpa_supplicant 检测错误密码

    选好了 wifi ssid,填了密码,生成新配置文件,重启了wpa_supplicant,怎么知道输入的密码对不对,如果不对有什么体现? wpa_supplicant 前台运行时,打印信息中会有: W ...

  2. oracle 常用语句3

    - oracle 函数 select sign(-3),sign(3), sign(0) from dual; select ceil(3.7) from dual; select floor(3.7 ...

  3. 面试 HTTP和HTML 浏览器

    HTTP和HTML 浏览器 #说一下http和https #参考回答: https的SSL加密是在传输层实现的. (1)http和https的基本概念 http: 超文本传输协议,是互联网上应用最为广 ...

  4. redis 五种常见攻击方法

    如果需要大佬写好的脚本,可以直接去github上面搜 参考文章:https://www.cnblogs.com/wineme/articles/11731612.html    https://www ...

  5. Open SSH CVE-2020-15778

    Open SSH OpenSSH 是用于使用 SSH 协议进行远程登录的一个开源实现.通过对交互的流量进行加密防止窃听,连接劫持以及其他攻击.OpenSSH 由 OpenBSD 项目的一些开发人员开发 ...

  6. Maven之继承

    这里我还是将通过一个例子来了解一下Maven继承的初步使用配置.还是使用三个工程项目Project-Parent.Project-C和Project-D来进行说明,三个项目关系如下: <?xml ...

  7. 5. 穿过拥挤的人潮,Spring已为你制作好高级赛道

    目录 ✍前言 版本约定 ✍正文 默认转换器注册情况 StreamConverter 代码示例 使用场景 兜底转换器 ObjectToObjectConverter part1:快速返回流程 part2 ...

  8. Win10-1909删除自带的微软输入法,添加美式键盘

    删除自带     输入法切换          

  9. C#中Newtonsoft.Json 序列化和反序列化 时间格式

    步骤 引用 using Newtonsoft.Json; using Newtonsoft.Json.Converters; 格式配置 IsoDateTimeConverter timeFormat ...

  10. 10天,从.Net转Java,并找到月薪2W的工作(三)

    放弃Offer之后,压力一天比一天打 好点的公司,除了技术追根问底,还对你平时代码实践问的很多.比如问你,redis缓存一般设置多大.问你项目内容的细节,业务流程. 集合.锁.Jvm.多线程高并发.微 ...