目录
2.2、创建一个终结符表达式,实现抽象表达式,重写解释器方法。
2.5、创建一个客户端,测试类 TestInterpreDesign.java
什么叫解释器模式?
解释器模式(Interpreter Pattern)提供了评估语言的语法或表达式的方式,它属于行为型模式。这种模式实现了一个表达式接口,该接口解释一个特定的上下文。这种模式被用在 SQL 解析、符号处理引擎等。
计算机用来解释句子或表达式。当我们需要编写一系列处理这种需求的代码时,首先要知道句子或表达式的结构,要有一个表达式或句子的内部表示。可以理解为将相似性质的对象集合在一起。
解释器模式定义语法的表示以及该语法的对应解释。
解释器模式使用组合模式来定义对象结构的内部表示。
图 2-1
解释器模式由4个部分组成:
这里可能有人还是不太明白啥叫终结符表达式,啥叫非终结符表达式。下面我举例说明一下:
TerminalExpression(终结符表达式)
实现文法中与终结符有关的解释操作。文法中每一个终结符都有一个具体的终结符表达式与之对应。比如我们的 A=B+C 运算,B和C就是终结符,对应的解析 B 和 C 的解释器就是终结符表达式。
NonTerminalExpression(非终结符表达式)
实现文法中与非终结符有关的解释操作。文法中的每一条规则都对应一个非终结符表达式。非终结符表达式一般是文法中的运算符或者关键字,如 A=B+C 中 + 号就是非终结符表达式,解析 + 号的解释器就是一个非终结符表达式。
我们利用解释器模式来解析带有一个变量的简单函数
。
波兰表示法也叫前缀表示法,我们普通的表示法叫中缀表示法,所以逆波兰就是后缀表示法。
这里我们为了简单,选择 逆波兰表示法(Reverse Polish notation,RPN,或逆波兰记法),在逆波兰记法中,所有操作符置于操作数的后面,因此也被称为后缀表示法。
这里顺道普及一下 逆波兰表示法的小知识哈……这种表示方法的好处就是不需要使用括号。
逆波兰表达式的解释器一般是基于堆栈的。
解释过程一般是:操作数入栈;遇到操作符时,操作数出栈,求值,将结果入栈;当一遍后,栈顶就是表达式的值。因此逆波兰表达式的求值使用堆栈结构很容易实现,和能很快求值。
package com.zhaoyanfei.designpattern.InterpreterPattern;
/**
* 抽象表达式接口 Expression
* @Date 2022年10月15日
* @author zhaoYanFei
*
*/
public interface Expression {
public float interpret();
}
Number 数字类解释所有数字。
package com.zhaoyanfei.designpattern.InterpreterPattern;
/**
* @description 终结符表达式 Number
* @Date 2022年10月15日
* @author zhaoYanFei
*
*/
public class Number implements Expression {
private float number;
public Number(float number) {
this.number = number;
}
@Override
public float interpret() {
return number;
}
}
2.3、创建一个非终结符表达式,实现抽象表达式,重写解释器方法。
Operate 操作符类就是我们的组合表达式。
这里我们写一个加法和一个减法运算的表达式。
package com.zhaoyanfei.designpattern.InterpreterPattern;
/**
*
* @description 加法(操作符)运算的非终结符表达式 Plus
* @Date 2022年10月15日
* @author zhaoYanFei
*
*/
public class Plus implements Expression {
Expression left;
Expression right;
public Plus(Expression left,Expression right) {
this.left = left;
this.right = right;
}
@Override
public float interpret() {
return left.interpret() + right.interpret();
}
}
类似地实现一个减法操作符的非终结表达式
package com.zhaoyanfei.designpattern.InterpreterPattern;
/**
*
* @description 减法(操作符)运算的非终结符表达式 Minus
* @Date 2022年10月15日
* @author zhaoYanFei
*
*/
public class Minus implements Expression {
Expression left;
Expression right;
public Minus(Expression left,Expression right) {
//比如:逆波兰运算法,这里需要注意。3 2 -,表示3-2的运算,这里由于栈是先进后出,所以pop先取出2(在栈的左边),3(栈的的右边)
this.left = left;
this.right = right;
}
@Override
public float interpret() {
//减法运算时,这里需要注意顺序
return right.interpret() - left.interpret();
}
}
2.4、创建一个环境类,定义解析文法。
现在我们定义上下文环境的类,也就是构建我们的语法树。
这里顺道解释一下栈的基本概念。以下是来源百度百科的定义
栈(stack)在计算机科学中是限定仅在表尾进行插入或删除操作的线性表。 栈是一种数据结构,它按照后进先出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据。 栈是只能在某一端插入和删除的特殊线性表。用桶堆积物品,先堆进来的压在底下,随后一件一件往上堆。取走时,只能从上面一件一件取。读和取都在顶部进行,底部一般是不动的。 栈就是一种类似桶堆积物品的数据结构,进行删除和插入的一端称栈顶,另一端称栈底。 插入一般称为进栈,删除则称为退栈。 栈也称为后进先出表。
Java 中栈 java.util.Stack 的用法:
方法 | 修饰符和类型 | 方法描述 |
---|---|---|
empty() | boolean | 测试堆栈是否为空 |
push(E item) | E | 把元素压入堆栈顶部 |
pop() | E | 移除堆栈顶部的对象,并作为此函数的值返回该对象 |
peek() | E | 查看堆栈顶部的对象,但不从堆栈中移除它 |
search(Object o) | int | 返回对象在堆栈中的位置,以1为基数 |
下面就是我们具体上下文环境类的代码:
package com.zhaoyanfei.designpattern.InterpreterPattern;
import java.util.Stack;
/**
*
* @description 上下文环境 ExpressionContext
* @Date 2022年10月15日
* @author zhaoYanFei
*
*/
public class ExpressionContext {
public float calculate(String expression) {
//初始一个空栈
Stack<Expression> stack = new Stack<Expression>();
float result = 0;
for(String token : expression.split(" ")) {
if(isOperate(token)) {//判断是操作符,则从栈中取出之前入栈的值作为左边表达式
Expression exp = null;
if(token.equals("+")) {
exp = stack.push(new Plus(stack.pop(), stack.pop()));
}else if(token.equals("-")) {
exp = stack.push(new Minus(stack.pop(), stack.pop()));
}
if(exp!=null) {
result = exp.interpret();
stack.push(new Number(result));
}
}
if(isNumber(token)) {//判断是数值,则直接进栈
stack.push(new Number(Float.parseFloat(token)));
}
}
return result;
}
/**
* 判断元素是否为数值
* @param token
* @return
*/
private boolean isNumber(String token) {
try {
Float.parseFloat(token);
return true;
} catch (NumberFormatException e) {
return false;
}
}
/**
* 判断元素是否为操作符
* @param token
* @return
*/
private boolean isOperate(String token) {
if("+".equals(token) || "-".equals(token)) {
return true;
}else {
return false;
}
}
}
package com.zhaoyanfei.designpattern.InterpreterPattern;
/**
*
* @description 客户端测试类 TestInterpreDesign
* @Date 2022年10月15日
* @author zhaoYanFei
*
*/
public class TestInterpreDesign {
public static void main(String[] args) {
ExpressionContext expressionContext = new ExpressionContext();
System.out.println(expressionContext.calculate("2 3 +"));
System.out.println(expressionContext.calculate("4 3 -"));
System.out.println(expressionContext.calculate("4 3 - 2 +"));
}
}
解释器模式适用于表达式被解释并转换为其内部表示的情况。内部表示是基于组合模式的,因此解释器模式不适用于复杂的语法。
Java 在 java.util.Parser 中实现了解释器模式,它用于解释正则表达式。
//定义一个正则表达式,任意个a字母和一个b字母
Pattern p = Pattern.compile("a*b");
Matcher matcher = p.matcher("aab");
boolean matches = matcher.matches();
System.out.println(matches);
注:解释器模式在实际项目中很少用到,Java本身定义了很多解释器,比如上面提到的Pattern。
优点
缺点