java设计模式-----16、解释器模式
概念:
Interpreter模式也叫解释器模式,是行为模式之一,它是一种特殊的设计模式,它建立一个解释器,对于特定的计算机程序设计语言,用来解释预先定义的文法。简单地说,Interpreter模式是一种简单的语法解释器构架。
解释器模式应用场景
当有一个语言需要解释执行, 并且你可将该语言中的句子表示为一个抽象语法树时,可使用解释器模式。而当存在以下情况时该模式效果最好:
1、该文法简单,对于复杂的文法, 文法的类层次变得庞大而无法管理。此时语法分析程序生成器这样的工具是更好的选择。它们无需构建抽象语法树即可解释表达式, 这样可以节省空间而且还可能节省时间。
2、效率不是一个关键问题,最高效的解释器通常不是通过直接解释语法分析树实现的, 而是首先将它们转换成另一种形式。例如,正则表达式通常被转换成状态机。但即使在这种情况下, 转换器仍可用解释器模式实现, 该模式仍是有用的。
结构图
解释器模式的角色和职责
1、Context:解释器上下文环境类。用来存储解释器的上下文环境,比如需要解释的文法等,简单来说,它的任务一般是用来存放文法中各个终结符所对应的具体值,比如R=R1+R2,给R1赋值100,给R2赋值200,这些信息需要存放到环境中。
2、AbstractExpression:解释器抽象类。抽象表达式,声明一个所有的具体表达式都需要实现的抽象接口;这个接口主要是一个interpret()方法,称做解释操作。
3、Terminal Expression:终结符表达式,解释器具体实现类,实现了抽象表达式所要求的接口;文法中的每一个终结符都有一个具体终结表达式与之相对应。比如公式R=R1+R2,R1和R2就是终结符,对应的解析R1和R2的解释器就是终结符表达式。
4、Nonterminal Expression:非终结符表达式,解释器具体实现类,文法中的每一条规则都需要一个具体的非终结符表达式,非终结符表达式一般是文法中的运算符或者其他关键字,比如公式R=R1+R2中,“+"就是非终结符,解析“+”的解释器就是一个非终结符表达式。
5、Client:客户端,指的是使用解释器的客户端,通常在这里将按照语言的语法做的表达式转换成使用解释器对象描述的抽象语法树,然后调用解释操作。
举一个计算器的例子
首先,新建一个Context,通过一个map来存储上下文信息
/*
* 解释器上下文环境类
*/
public class Context {
private Map<Character, Double> variable; public Map<Character, Double> getVariable() {
if (this.variable == null)
{
this.variable = new HashMap<Character, Double>();
}
return this.variable;
} //返回结果
public void setVariable(Map<Character, Double> variable) {
this.variable = variable;
}
}
再新建AbstractExpression
/*
* 抽象表达式
*/
public abstract class Expression {
public abstract double Interpret(Context context);
}
新建Terminal Expression
/*
* 变量,终结符表达式
*/
public class VariableExpression extends Expression {
private char key; public VariableExpression(char key)
{
this.key = key;
} @Override
public double Interpret(Context context) {
return context.getVariable().get(key);
}
}
再新建Nonterminal Expression,因为这其中包含许多不同的实现功能,比如加减乘除,所以我们先新建一个抽象类,再新建不同的实现类
抽象运算符号解析器
/*
* 操作符,非终结符表达式
*/
public abstract class OperatorExpression extends Expression {
protected Expression left;
protected Expression right; public OperatorExpression(Expression left, Expression right)
{
this.left = left;
this.right = right;
}
}
具体的解析器
/*
* 加法解析器
*/
public class AddExpression extends OperatorExpression { public AddExpression(Expression left, Expression right) {
super(left, right);
} @Override
public double Interpret(Context context) {
return this.left.Interpret(context) + this.right.Interpret(context);
}
}
/*
* 减法解析器
*/
public class SubExpression extends OperatorExpression { public SubExpression(Expression left, Expression right) {
super(left, right);
} @Override
public double Interpret(Context context) {
return this.left.Interpret(context) - this.right.Interpret(context);
}
}
/*
* 乘法解析器
*/
public class MulExpression extends OperatorExpression { public MulExpression(Expression left, Expression right) {
super(left, right);
} @Override
public double Interpret(Context context) {
return this.left.Interpret(context) * this.right.Interpret(context);
}
}
/*
* 除法解析器
*/
public class DivExpression extends OperatorExpression { public DivExpression(Expression left, Expression right) {
super(left, right);
} @Override
public double Interpret(Context context) {
return this.left.Interpret(context) / this.right.Interpret(context);
}
}
接下来,新建一个解析器封装类
解析器封装类,这个类是根据迪米特法则进行封装,他并不是解释器模式的组成部分,目的是让Client只与功能模块打交道,不涉及到具体的实现,相当于Facade(外观模式)
public class Calculator {
private String expression;
private Context context; public Calculator(String expression)
{
this.expression = expression;
this.context = new Context();
} public double Calculate()
{
char[] vars = this.expression.toCharArray();
for(char c : vars){
if (c == '+' || c == '-' || c == '*' || c == '/')
{
continue;
}
if (!this.context.getVariable().containsKey(c))
{
System.out.println("请输入一个数字:");
System.out.print(c+"=");
Scanner sn = new Scanner(System.in);
String readLine = sn.next();
this.context.getVariable().put(c, Double.parseDouble(readLine));
}
}
Expression left = new VariableExpression(vars[0]);
Expression right = null;
Stack<Expression> stack = new Stack<Expression>();
stack.push(left);
for (int i = 1; i < vars.length; i += 2)
{
left = stack.pop();
right = new VariableExpression(vars[i + 1]);
switch (vars[i])
{
case '+':
stack.push(new AddExpression(left, right));
break;
case '-':
stack.push(new SubExpression(left, right));
break;
case '*':
stack.push(new MulExpression(left, right));
break;
case '/':
stack.push(new DivExpression(left, right));
break;
}
}
double value = stack.pop().Interpret(this.context);
stack.clear();
return value;
}
}
最后,是客户端,解释器的使用者
//Client客户端
public class Client {
public static void main(String[] args) {
Calculator c = new Calculator("a*b/c");
System.out.println("结果等于" + c.Calculate());
}
}
运行结果:
优点: 1、可扩展性比较好,灵活。 2、增加了新的解释表达式的方式。 3、易于实现简单文法。
缺点: 1、可利用场景比较少。 2、对于复杂的文法比较难维护。 3、解释器模式会引起类膨胀。 4、解释器模式采用递归调用方法。
java设计模式-----16、解释器模式的更多相关文章
- 折腾Java设计模式之解释器模式
解释器模式 解释器模式是类的行为模式.给定一个语言之后,解释器模式可以定义出其文法的一种表示,并同时提供一个解释器.客户端可以使用这个解释器来解释这个语言中的句子. 意图 给定一个语言,定义它的文法表 ...
- JAVA设计模式之解释器模式
在阎宏博士的<JAVA与模式>一书中开头是这样描述解释器(Interpreter)模式的: 解释器模式是类的行为模式.给定一个语言之后,解释器模式可以定义出其文法的一种表示,并同时提供一个 ...
- 20.java设计模式之解释器模式
基本需求 实现四则运算,如计算a+b-c+d的值 先输入表达式的形式,如a+b-c+d,要求表达式正确 再分别输出a,b,c,d的值 最后求出结果 传统方案 编写一个方法,接收表达式的形式,根据用户输 ...
- 简单的介绍一下Java设计模式:解释器模式
目录 定义 意图 主要解决问题 优缺点 结构 示例 适用情况 定义 解释器模式是类的行为型模式,给定一个语言之后,解释器模式可以定义出其文法的一种表示,并同时提供一个解释器,客户端可以使用这个解释器来 ...
- Java设计模式之builder模式
Java设计模式之builder模式 今天学mybatis的时候,知道了SQLSessionFactory使用的是builder模式来生成的.再次整理一下什么是builder模式以及应用场景. 1. ...
- Java设计模式——装饰者模式
JAVA 设计模式 装饰者模式 用途 装饰者模式 (Decorator) 动态地给一个对象添加一些额外的职责.就增加功能来说,Decorator 模式相比生成子类更为灵活. 装饰者模式是一种结构式模式 ...
- 浅析JAVA设计模式之工厂模式(一)
1 工厂模式简单介绍 工厂模式的定义:简单地说,用来实例化对象,取代new操作. 工厂模式专门负责将大量有共同接口的类实例化.工作模式能够动态决定将哪一个类实例化.不用先知道每次要实例化哪一个类. 工 ...
- 乐在其中设计模式(C#) - 解释器模式(Interpreter Pattern)
原文:乐在其中设计模式(C#) - 解释器模式(Interpreter Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 解释器模式(Interpreter Pattern) 作 ...
- JAVA设计模式--装饰器模式
装饰器模式 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构.这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装. 这种模式创建了一个装饰 ...
- 折腾Java设计模式之建造者模式
博文原址:折腾Java设计模式之建造者模式 建造者模式 Separate the construction of a complex object from its representation, a ...
随机推荐
- udid iphone6 获取
http://www.udidregistration.org/how-to-find-udid-of-iphone-6.html
- JavaScript基础数组_布尔值_逻辑运算等(2)
day51 参考:https://www.cnblogs.com/liwenzhou/p/8004649.html 布尔值(Boolean) 区别于Python,true和false都是小写. var ...
- 关于finecms v5 会员头像 任意文件上传漏洞分析
看到我私藏的一个洞被别人提交到补天拿奖金,所以我干脆在社区这里分享,给大家学习下 本文原创作者:常威,本文属i春秋原创奖励计划,未经许可禁止转载! 1.定位功能 下载源码在本地搭建起来后,正常登陆了用 ...
- Sublime Text 格式化JSON-pretty json
1.安装install package 按control + `,打开命令输入框 输入一下命令: import urllib2,os; pf='Package Control.sublime-pack ...
- react-router-dom 手动控制路由跳转
基于 react-router 4.0 版本,我们想要通过 JS 手动控制路由跳转,分三步: 第一步:引入 propTypes const PropTypes = require('prop-type ...
- mysql-mmm
查看mmm集群状态: mmm_control show 给主机设置ip: mmm_control set_ip ip host 改变状态: mmm_control set_passive|active ...
- 【learning】 单调队列与单调栈用法详解
1.单调栈 单调栈是指一个栈内部的元素具有严格单调性的一种数据结构,分为单调递增栈和单调递减栈. 其具有以下两个性质: 1,满足栈底到栈顶的元素具有严格单调性. 2,满足栈的先进后出特性,越靠近栈顶的 ...
- 剑指offer四十七之求1+2+3+...+n
一.题目 求1+2+3+...+n,要求不能使用乘除法.for.while.if.else.switch.case等关键字及条件判断语句(A?B:C). 二.思路 1.需利用逻辑与的短路特性实现递归终 ...
- Linux驱动:SPI驱动编写要点
题外话:面对成功和失败,一个人有没有“冠军之心”,直接影响他的表现. 几周前剖析了Linux SPI 驱动框架,算是明白个所以然,对于这么一个庞大的框架,并不是每一行代码都要自己去敲,因为前人已经把这 ...
- 【Java并发编程】:volatile变量修饰符
volatile用处说明 在JDK1.2之前,java的内存模型实现总是从主存(即共享内存)读取变量,是不需要进行特别的注意的.而随着JVM的成熟和优化,现在在多线程环境下volatile关键 ...