基本需求

  • 实现四则运算,如计算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. Redis安装教程及安装报错解决方案(大佬勿喷)

    安装环境:CentOS7 Redis版本:redis-6.0.9.tar.gz 依次按照以下顺序执行: 1. [root@localhost ~]# wget https://download.red ...

  2. PHP功能代码片段

    1.连接MYSQL数据库代码  <?php  $connec=mysql_connect("localhost","root","root&qu ...

  3. [UWP] - Adaptive Trigger Featurede的使用方法及效果

    XAML代码: <Page.Resources> <!-- DataTemplate to use in the portrait layout. --> <DataTe ...

  4. 【磁盘/文件系统】第五篇:CentOS7.x__btrfs文件系统详解

    前言: Btrfs文件系统是CentOS7.x系列系统上的技术预览版,但是现在还是有公司在使用. btrfs 文件系统(又称B-tree.Butter FS.Better FS等文件系统)   理解b ...

  5. python初学者-从小到大排序

    x=input("x=") y=input("y=") z=input("z=") if x>y: x,y=y,x if x>z ...

  6. 输入一个正整数 target ,输出所有和为 target 的连续正整数序列(至少含有两个数)

    package leetcode;import edu.princeton.cs.algs4.Cycle;import java.util.ArrayList;import java.util.Arr ...

  7. java中将文件夹里面的文件复制到指定的文件夹(java IO)

    //现在制定路径下创建名称为左侧的文件夹 public class Copy { public static void main(String[] args) { //原始文件地址 File srcF ...

  8. Spring源码深度解析之Spring MVC

    Spring源码深度解析之Spring MVC Spring框架提供了构建Web应用程序的全功能MVC模块.通过策略接口,Spring框架是高度可配置的,而且支持多种视图技术,例如JavaServer ...

  9. 关闭layer

    function closeBox() { var index = parent.layer.getFrameIndex(window.name); //获取当前窗体索引 parent.layer.c ...

  10. python-使用python获取一段录音

    需要安装pyaudio库. 直接上代码: import pyaudio import wave AUDIO_FILE = '录音文件.wav' def get_audio(filepath, audi ...