基本需求

  • 实现四则运算,如计算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. ES6、ES7、ES8、ES9、ES10新特性

    ES6新特性(2015) ES6的特性比较多,在 ES5 发布近 6 年(2009-11 至 2015-6)之后才将其标准化.两个发布版本之间时间跨度很大,所以ES6中的特性比较多. 在这里列举几个常 ...

  2. 【涂鸦物联网足迹】用煲仔饭来说明IaaS/PaaS/SaaS的区别

    最近在准备一些科普性的知识内容,发现大家对于一些基础性的知识概念还是有点模糊.今天先来简单介绍一下IaaS/PaaS/SaaS的区别~ 其实还有一个On-Premises(本地部署)的概念,也可以一并 ...

  3. WIN7环境下配置vscode c++环境

    目录 安装vscode 添加中文环境支持 添加c++支持 配置c++环境 安装MinGW 配置MinGW环境变量 配置vscode launch文件配置 task文件配置 可能出现的问题 安装vsco ...

  4. Spark-6-如何缓解消除数据倾斜

    1 尽量避免数据源的数据倾斜 比如数据源是Kafka 以Spark Stream通过DirectStream方式读取Kafka数据为例.由于Kafka的每一个Partition对应Spark的一个Ta ...

  5. 工具-python包-虚拟环境管理(99.4.1)

    @ 目录 1.第一种方法-virtualenv 2.第二种方法-pycharm 关于作者 1.第一种方法-virtualenv 1.安装 pip install virtualenv pip inst ...

  6. 个人微信公众号搭建Python实现 -个人公众号搭建-永久素材管理(14.3.5)

    @ 目录 1.说明 2.上传素材 3.获取素材列表 关于作者 1.说明 个人微信公众号开发的功能有限,因为很多权限没有,但支持上传永久素材,具体查看微信公众号文档 这里的请求都要将本地IP地址放到微信 ...

  7. 用Python写个开心消消乐小游戏

    本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理 提到开心消消乐这款小游戏,相信大家都不陌生,其曾在 2015 年获得过玩家最喜爱的移动单机游戏奖,受 ...

  8. 利用Python将PDF文档转为MP3音频

    1. 转语音工具 微信读书有一个功能,可以将书里的文字转换为音频,而且声音优化的不错,比传统的机械朗读听起来舒服很多. 记得之前看到过Python有一个工具包,可以将文字转换为语音,支持英文和中文,而 ...

  9. Centos8 Docker+Nginx部署Asp.Net Core Nginx正向代理与反向代理 负载均衡实现无状态更新

    首先了解Nginx 相关介绍(正向代理和反向代理区别) 所谓代理就是一个代表.一个渠道: 此时就涉及到两个角色,一个是被代理角色,一个是目标角色,被代理角色通过这个代理访问目标角色完成一些任务的过程称 ...

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

    删除自带     输入法切换