概念:

  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、解释器模式的更多相关文章

  1. 折腾Java设计模式之解释器模式

    解释器模式 解释器模式是类的行为模式.给定一个语言之后,解释器模式可以定义出其文法的一种表示,并同时提供一个解释器.客户端可以使用这个解释器来解释这个语言中的句子. 意图 给定一个语言,定义它的文法表 ...

  2. JAVA设计模式之解释器模式

    在阎宏博士的<JAVA与模式>一书中开头是这样描述解释器(Interpreter)模式的: 解释器模式是类的行为模式.给定一个语言之后,解释器模式可以定义出其文法的一种表示,并同时提供一个 ...

  3. 20.java设计模式之解释器模式

    基本需求 实现四则运算,如计算a+b-c+d的值 先输入表达式的形式,如a+b-c+d,要求表达式正确 再分别输出a,b,c,d的值 最后求出结果 传统方案 编写一个方法,接收表达式的形式,根据用户输 ...

  4. 简单的介绍一下Java设计模式:解释器模式

    目录 定义 意图 主要解决问题 优缺点 结构 示例 适用情况 定义 解释器模式是类的行为型模式,给定一个语言之后,解释器模式可以定义出其文法的一种表示,并同时提供一个解释器,客户端可以使用这个解释器来 ...

  5. Java设计模式之builder模式

    Java设计模式之builder模式 今天学mybatis的时候,知道了SQLSessionFactory使用的是builder模式来生成的.再次整理一下什么是builder模式以及应用场景. 1. ...

  6. Java设计模式——装饰者模式

    JAVA 设计模式 装饰者模式 用途 装饰者模式 (Decorator) 动态地给一个对象添加一些额外的职责.就增加功能来说,Decorator 模式相比生成子类更为灵活. 装饰者模式是一种结构式模式 ...

  7. 浅析JAVA设计模式之工厂模式(一)

    1 工厂模式简单介绍 工厂模式的定义:简单地说,用来实例化对象,取代new操作. 工厂模式专门负责将大量有共同接口的类实例化.工作模式能够动态决定将哪一个类实例化.不用先知道每次要实例化哪一个类. 工 ...

  8. 乐在其中设计模式(C#) - 解释器模式(Interpreter Pattern)

    原文:乐在其中设计模式(C#) - 解释器模式(Interpreter Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 解释器模式(Interpreter Pattern) 作 ...

  9. JAVA设计模式--装饰器模式

    装饰器模式 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构.这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装. 这种模式创建了一个装饰 ...

  10. 折腾Java设计模式之建造者模式

    博文原址:折腾Java设计模式之建造者模式 建造者模式 Separate the construction of a complex object from its representation, a ...

随机推荐

  1. Sentinel 哨兵 实现redis高可用

    本文链接:http://www.cnblogs.com/zhenghongxin/p/8885879.html 我们知道redis是有主从复制的,例如下图: 但如果master主进程挂掉之后,没有sl ...

  2. python 匿名函数 lambda 的使用

    在python中,lambda允许用户快速定义单行函数,当然用户也可以按照典型的函数定义完成函数.lambda的目的就是简化用户定义使用函数的过程. In [6]: s = lambda x: x+1 ...

  3. CSS初窥

  4. 面试题:js如何渲染十万条数据并不卡住界面

    这道题考察了如何在不卡住页面的情况下渲染数据,也就是说不能一次性将几万条都渲染出来,而应该一次渲染部分 DOM,那么就可以通过 requestAnimationFrame 来每 16 ms 刷新一次. ...

  5. 【DB2】How to resolve SQL20249N the statement was not processed with error

    相关链接 https://vinaysdb2blog.blogspot.com/2017/11/how-to-resolve-sql20249n-statement-was-not-processed ...

  6. python中使用eval() 和 ast.literal_eval()的区别 分类: Python 2015-05-11 15:21 1216人阅读 评论(0) 收藏

    eval函数在python中做数据类型的转换还是很有用的.它的作用就是把数据还原成它本身或者是能够转化成的数据类型. 那么eval和ast.literal_val()的区别是什么呢? eval在做计算 ...

  7. docker - 修改镜像/容器文件或者 "Docker root dir" 的在宿主机上的存储位置

    背景 之前在使用docker的时候,由于启动container的时候用的是默认的mount(路径为 /var/lib/docker),这个目录对应的硬盘空间有限,只有200G左右.现在随着程序运行,有 ...

  8. Office Visio 201*安装详细步骤并激活

    不多说直接上干货! 初步了解:  Visio的百度百科:http://baike.baidu.com/link?url=tNv_gqhhVKcurpP8kvh4ylkknc5JQLIm6bGmQVxi ...

  9. Java虚拟机(三):JVM垃圾回收机制

    概述 垃圾收集 Garbage Collection 通常被称为“GC”,它诞生于1960年 MIT 的 Lisp 语言,经过半个多世纪,目前已经十分成熟了. jvm 中,程序计数器.虚拟机栈.本地方 ...

  10. tomcat在eclipse创建过程分析

    在本地eclipse上创建一个tomcat server 即tomcat服务器时, 会复制一份tomca安装目录中的conf文件下的配置文件到这个tomcat server目录下 这个tomcat s ...