计算器中,我们输入“20 + 10 - 5”,计算器会得出结果25并返回给我们。可你有没有想过计算器是怎样完成四则运算的?或者说,计算器是怎样识别你输入的这串字符串信息,并加以解析,然后执行之,得出结果?这里就引出了今天我想要介绍的一个设计模式----解释器模式。


1.解释器模式

解释器模式(Interpreter Pattern),给定一个语言,定义它的语法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。    ----《大话设计模式》

解释器模式的基本思想是:对于每个符号,都定义一个表达式class表示它。通常情况下由这些class组成一个表达式的抽象语义树(Abstract Syntax Tree, AST),如下图所示:

Expr表示一个一个的表达式class,其中,黄色的表示终结符表达式(TermialExpression),粉色的表示非终结符表达式(NonTermialExpression)。从图中看,所谓终结符表达式就是就是叶节点,它不再有树枝;非终结符表达式有分支节点。我们可以以一个简单的算术表达式来说明解释器模式:

x + y - z

在以上表达式中,符号有  x、y、z、+、-  一共5个符号,其中x,y,z实际上都是变量,那么我们分为三种语义:变量Variable,  加法Plus, 减法Minus。 其实在计算机中一般是用后缀表达式来进行算数运算的,上面的表达式转换为后缀表达式是:

x y z - +

后缀、中缀、前缀表达式之间转换是有方法的,本文不做详细解释,按照后缀表达式,x + y - z运算的顺序实际上是这样的:

  1. y - z.   --> v
  2. x + v

即 x + (y - z)

所以实际过程中,我们会生成这样由5个表达式实例组成的AST:

实际上这样的树形结构就是组合模式的应用。可参考组合模式

一般地,解释器模式的UML类图如下:

图中,

TerminalExpression 即终结符表达式,没有子表达式, 在树形图中为黄色元素,像变量、常量等通常为终结符表达式。

NonTerminalExpression 即非终结符表达式,有子表达式, 在树形图中为粉色元素,像加法(plus),减法(minus)等通常包含多个子表达式(包含x, y)。

Context 环境的上下文信息,在本文中储存变量的信息。


2.代码实现

下面以实现一个支持加减法的程序为例来进一步直观展示解释器模式的应用。

抽象类或者接口,定义了interpret()

 /**
* 抽象表达式
*/
interface Expression {
int interpret(Map<String, Integer> context);
}

实现加法表达式, right和left代表“+”符号的右边和左边的表达式, 因为包含其它表达式,所以为非终结符表达式。

/**
* 定义了加法的法则
*/
class Plus implements Expression { Expression right;
Expression left; public Plus(Expression right, Expression left) {
this.right = right;
this.left = left;
} @Override
public int interpret(Map<String, Integer> context) {
return right.interpret(context) + left.interpret(context);
}
}

实现减法,规则与加法略同

/**
* 定义了减法的法则
*/
class Minus implements Expression { Expression right;
Expression left; public Minus(Expression r, Expression l) {
right = r;
left = l;
}
@Override
public int interpret(Map<String, Integer> context) {
return left.interpret(context) - right.interpret(context);
}
}

实现变量表达式,name是变量的名称,比如x,y,z,其值保存在context中,因为变量表达式不会再包含子表达式,所以其为终结符表达式。

class Variable implements Expression {

    String name;

    public Variable(String name) {
this.name = name;
} @Override
public int interpret(Map<String, Integer> context) {
return context.getOrDefault(name, 0);
}
}

转化并调用interpret(), 这里提供了parse()方法,将后缀表达式转化为AST,后缀表达式由页面或者客户端界面用户输入的中缀表达式转化而来,这里不做详解。

我将AST临时保存在stack中,将其出栈即可获取定义了整个AST的表达式实例,调用interpret即可。

public class InterpreterDemo {

    public static Expression parseToken(String token, ArrayDeque<Expression> stack) {
Expression left, right;
switch (token) {
case "+":
right = stack.pop();
left = stack.pop();
return new Plus(right, left);
case "-":
right = stack.pop();
left = stack.pop();
return new Minus(right, left);
default:
return new Variable(token); }
} public static Expression parse(String expression) {
ArrayDeque<Expression> stack = new ArrayDeque<>();
for (String token : expression.split(" ")) {
stack.push(parseToken(token, stack));
}
return stack.pop();
}
public static void main(String[] args) {
Expression expr = parse("x y z - +");
//context
Map<String, Integer> context = new HashMap<>();
context.put("x", 20);
context.put("y", 10);
context.put("z", 5); //计算
int result = expr.interpret(context);
System.out.println(result);
}
}

输出结果:25


3.总结

尽管在开发过程中使用解释器模式的场景不是很多,诸如正则表达式这类内置的功能使用解释器来解析语法得出我们想要的结果,因此,解释器模式其实就可以用下面这张图来理解:

输入一段表达式或者语句,按照规定的规则解释,将结果输出。一般情况下如下场景可以考虑使用解释器模式:

  1. 需要为一个简单的”语言“定义一套语法,
  2. 这门”语言“中的”句子“可以被解释。

这里,”语言“可以是编程语言,自然语言(或者人工构造语言),算术运算,某种规则等等。。只要有规律可循以定义语法或表达式。解释器模式的解决方案可以描述为:

  • 定义Expression class的继承结构,并实现一个解释方法 interpret().
  • 将各个Expression实例,组合成AST,以表示某一个句子或表达式。
  • 使用AST中某个实例(通常为根部实例),调用interpret()方法翻译句子或表达式。

注意,解释器interpret()并不会将用户输入的原始句子转换为AST, 所以,调用interpret(), 首先需要先把原始语句转换为AST,通常可以提供一个转换器类parser,或者由客户端自行转换后再调用interpret().本例中,我在客户端封装了一个parse()方法来进行把语句"x y z - +"转换为AST。

Java设计模式----解释器模式的更多相关文章

  1. JAVA 设计模式 解释器模式

    用途 解释器模式 (Interpreter) 定义一个语言,定义它的文法的一种表示. 并定义一个解释器,这个解释器使用该表示来解释语言中的句子. 解释器模式是一种行为型模式. 结构

  2. Java设计模式—解释器模式&迭代器模式简介

       解释器模式在实际的系统开发中使用得非常少,因为它会引起效率.性能以及维护等问题,一般在大中型的框架型项目能够找到它的身影,如一些数据分析工具.报表设计工具.科学计算工具等,若你确实遇到" ...

  3. Java设计模式-解释器模式(Interpreter)

    解释器模式是我们暂时的最后一讲,一般主要应用在OOP开发中的编译器的开发中,所以适用面比较窄. Context类是一个上下文环境类,Plus和Minus分别是用来计算的实现,代码如下: public ...

  4. Java设计模式——组合模式

    JAVA 设计模式 组合模式 用途 组合模式 (Component) 将对象组合成树形结构以表示“部分-整体”的层次结构.组合模式使得用户对单个对象和组合对象的使用具有唯一性. 组合模式是一种结构型模 ...

  5. java设计模式--单列模式

    java设计模式--单列模式 单列模式定义:确保一个类只有一个实例,并提供一个全局访问点. 下面是几种实现单列模式的Demo,每个Demo都有自己的优缺点: Demo1: /** * 单列模式需要满足 ...

  6. 3.java设计模式-建造者模式

    Java设计模式-建造者模式 在<JAVA与模式>一书中开头是这样描述建造(Builder)模式的: 建造模式是对象的创建模式.建造模式可以将一个产品的内部表象(internal repr ...

  7. Java设计模式-代理模式之动态代理(附源代码分析)

    Java设计模式-代理模式之动态代理(附源代码分析) 动态代理概念及类图 上一篇中介绍了静态代理,动态代理跟静态代理一个最大的差别就是:动态代理是在执行时刻动态的创建出代理类及其对象. 上篇中的静态代 ...

  8. Java设计模式——外观模式

    JAVA 设计模式 外观模式 用途 外观模式 (Facade) 为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用. 外观模式是一种结构型模式. 结构

  9. 【设计模式】Java设计模式 -工厂模式

    [设计模式]Java设计模式 -工厂模式 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 一个有梦有戏的人 @怒放吧德德 分享学习心得,欢迎指正,大家一起学习成长! 目 ...

随机推荐

  1. Error importing tensorflow. Unless you are using bazel version `CXXABI_1.3.8' not found

    I have re-installed Anaconda2. And I got the following error when 'python -c 'import tensorflow'' &g ...

  2. DRF框架简介(第一天)

    1.drf框架全称 djangorestframework 1.如何安装drf框架: pip3 install djangorestframework #drf框架其实就是一个app称之为drf #d ...

  3. Object.defineProperty(o,p,descriptor ) 理解应用

    1. Object.defineProperty  在一个对象上定义一个新属性,或修改一个已经存在的属性, 最终返回这个对象. var __define = this.__define || func ...

  4. jQuery自定义alert,confirm方法及样式

    学过JavaScript的都知道,alert().confirm()都是window对象特有的方法,而这两个方法我们平时使用的频率也很高,但是比较扎心的就是他自带的样式太... 因此,我整理了一个比较 ...

  5. 前端 跨Area后Cookie无法访问

    创建两个区域,一个是User,一个是Manage. User区域有两个页面,index1,和index2 User区域: index1:负责写入cookie index2:负责读取cookie Man ...

  6. ELK Redis高性能加速

    1.下载redis并安装好 wget http://download.redis.io/releases/redis-2.8.13.tar.gz tar zxf redis-.tar.gz cd re ...

  7. Jquery中父,子页面之间元素获取及方法调用

    一.jquery 父.子页面之间页面元素的获取,方法的调用: 1. 父页面获取子页面元素: 格式:$("#iframe的ID").contents().find("#if ...

  8. 原生js实现Base64编码解码

    注:ie10+ var str = window.btoa("liusong"); console.log(str); var s = window.atob("bGl1 ...

  9. Linux学习-linux系统下安装jdk和tomcat,以及遇到的问题清单

    安装JDK 1. 在usr目录下建立java安装目录 cd /usr mkdir java   2.下载jdk包 登录网址:http://www.oracle.com/technetwork/java ...

  10. cdnbest节点动态ip配置教程

    1.安装节点后,在未初始化里初始化节点,如下图操作,要选择动态ip(注:动态ip节点不支持添加辅ip) 服务器如果是动态ip,选择了动态ip选项,节点在自动更换了新的ip后,在节点列表里的ip和dns ...