Java设计模式----解释器模式
计算器中,我们输入“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运算的顺序实际上是这样的:
- y - z. --> v
- 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.总结
尽管在开发过程中使用解释器模式的场景不是很多,诸如正则表达式这类内置的功能使用解释器来解析语法得出我们想要的结果,因此,解释器模式其实就可以用下面这张图来理解:

输入一段表达式或者语句,按照规定的规则解释,将结果输出。一般情况下如下场景可以考虑使用解释器模式:
- 需要为一个简单的”语言“定义一套语法,
- 这门”语言“中的”句子“可以被解释。
这里,”语言“可以是编程语言,自然语言(或者人工构造语言),算术运算,某种规则等等。。只要有规律可循以定义语法或表达式。解释器模式的解决方案可以描述为:
- 定义Expression class的继承结构,并实现一个解释方法 interpret().
- 将各个Expression实例,组合成AST,以表示某一个句子或表达式。
- 使用AST中某个实例(通常为根部实例),调用interpret()方法翻译句子或表达式。
注意,解释器interpret()并不会将用户输入的原始句子转换为AST, 所以,调用interpret(), 首先需要先把原始语句转换为AST,通常可以提供一个转换器类parser,或者由客户端自行转换后再调用interpret().本例中,我在客户端封装了一个parse()方法来进行把语句"x y z - +"转换为AST。
Java设计模式----解释器模式的更多相关文章
- JAVA 设计模式 解释器模式
用途 解释器模式 (Interpreter) 定义一个语言,定义它的文法的一种表示. 并定义一个解释器,这个解释器使用该表示来解释语言中的句子. 解释器模式是一种行为型模式. 结构
- Java设计模式—解释器模式&迭代器模式简介
解释器模式在实际的系统开发中使用得非常少,因为它会引起效率.性能以及维护等问题,一般在大中型的框架型项目能够找到它的身影,如一些数据分析工具.报表设计工具.科学计算工具等,若你确实遇到" ...
- Java设计模式-解释器模式(Interpreter)
解释器模式是我们暂时的最后一讲,一般主要应用在OOP开发中的编译器的开发中,所以适用面比较窄. Context类是一个上下文环境类,Plus和Minus分别是用来计算的实现,代码如下: public ...
- Java设计模式——组合模式
JAVA 设计模式 组合模式 用途 组合模式 (Component) 将对象组合成树形结构以表示“部分-整体”的层次结构.组合模式使得用户对单个对象和组合对象的使用具有唯一性. 组合模式是一种结构型模 ...
- java设计模式--单列模式
java设计模式--单列模式 单列模式定义:确保一个类只有一个实例,并提供一个全局访问点. 下面是几种实现单列模式的Demo,每个Demo都有自己的优缺点: Demo1: /** * 单列模式需要满足 ...
- 3.java设计模式-建造者模式
Java设计模式-建造者模式 在<JAVA与模式>一书中开头是这样描述建造(Builder)模式的: 建造模式是对象的创建模式.建造模式可以将一个产品的内部表象(internal repr ...
- Java设计模式-代理模式之动态代理(附源代码分析)
Java设计模式-代理模式之动态代理(附源代码分析) 动态代理概念及类图 上一篇中介绍了静态代理,动态代理跟静态代理一个最大的差别就是:动态代理是在执行时刻动态的创建出代理类及其对象. 上篇中的静态代 ...
- Java设计模式——外观模式
JAVA 设计模式 外观模式 用途 外观模式 (Facade) 为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用. 外观模式是一种结构型模式. 结构
- 【设计模式】Java设计模式 -工厂模式
[设计模式]Java设计模式 -工厂模式 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 一个有梦有戏的人 @怒放吧德德 分享学习心得,欢迎指正,大家一起学习成长! 目 ...
随机推荐
- Django02-路由系统urls
一.路由配置系统(URLconf) 分为:静态路由动态路由 1.URL配置 URL配置(URLconf)就像Django所支撑网站的目录.它的本质是URL与该URL调用的视图函数之间的映射表 语法: ...
- shell脚本运行java程序jar
在UBuntu上部署项目的时候,我们往往通过一段shell来启动程序,甚至是通过crontab定时任务来定时的调用java程序,但是很奇怪的一个问题就是,比如我写了一个如下的shell脚本: #!/b ...
- 转 cxgrid属性说明
TCXGRID控件:属性:ActiveLevel: 当前层BorderStyle: 窗口风格Color: 颜色FocusedView: 当前View;Font: 字体LevelTabs: 类似Page ...
- 吴裕雄 python深度学习与实践(16)
import struct import numpy as np import matplotlib.pyplot as plt dateMat = np.ones((7,7)) kernel = n ...
- 吴裕雄 python深度学习与实践(9)
import numpy as np import tensorflow as tf inputX = np.random.rand(100) inputY = np.multiply(3,input ...
- SpringBoot的spring-boot-starter有哪些(官方)
看完这些,你就知道每个spring-boot-starter依赖些什么东西了. 地址:https://github.com/spring-projects/spring-boot/tree/v2.1. ...
- echo不换行的实现
1. echo的参数中, -e表示开启转义, /c表示不换行: echo -e "please input a value:/c" 2. -n不换行: echo -n " ...
- poj 1639 最小k度限制生成树
题目链接:https://vjudge.net/problem 题意: 给各位看一下题意,算法详解看下面大佬博客吧,写的很好. 参考博客:最小k度限制生成树 - chty - 博客园 https:/ ...
- linux操作命令,批量注释#方法
用户,密码 1.修改密码:passwd 2.切换用户: su root 3.增加用户:adduesr+用户 4.root更改目录的权限:chown leopard:leopard data/ -R ...
- Pycharm中选择Python解释器
新建项目后,有时候Pycharm找不到Python解释器,如果找不到的话,就会报错.报错信息: No python interpreter configured for the project 找到P ...