解释器模式
解释器模式(Interpreter Pattern)提供了评估语言的语法或表达式的方式,它属于行为型模式。这种模式实现了一个表达式接口,该接口解释一个特定的上下文。这种模式被用在 SQL 解析、符号处理引擎等。
定义:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
为了解释一种语言,而为语言创建的解释器
模式和结构定义
- 抽象解释器(AbstractExpression/Expression):声明一个所有具体表达式都要实现的抽象接口(或者抽象类),接口中主要是一个interpret()方法,称为解释操作。具体解释任务由它的各个实现类来完成,具体的解释器分别由终结符解释器TerminalExpression和非终结符解释器NonterminalExpression完成。
- 终结符表达式(TerminalExpression):实现与文法中的元素相关联的解释操作,通常一个解释器模式中只有一个终结符表达式,但有多个实例,对应不同的终结符。终结符一半是文法中的运算单元,比如有一个简单的公式R=R1+R2,在里面R1和R2就是终结符,对应的解析R1和R2的解释器就是终结符表达式。
- 非终结符表达式(NonterminalExpression):文法中的每条规则对应于一个非终结符表达式,非终结符表达式一般是文法中的运算符或者其他关键字,比如公式R=R1+R2中,+就是非终结符,解析+的解释器就是一个非终结符表达式。非终结符表达式根据逻辑的复杂程度而增加,原则上每个文法规则都对应一个非终结符表达式。
- 环境角色(Context):这个角色的任务一般是用来存放文法中各个终结符所对应的具体值,比如R=R1+R2,我们给R1赋值100,给R2赋值200。这些信息需要存放到环境角色中,很多情况下我们使用Map来充当环境角色就足够了。
应用实例
实现一个简单的计算表达式解释器。
表达式:6 100 11 + *
先计算: 100 + 11 = 111
再把上一个计算的结果继续加入计算: 111 * 6 = 666
- Interpreter:解释器接口
- MultiInterpreter:乘法解释器实现
- AddInterpreter:加法解释器实现
- NumberInterpreter:数字转换解释器实现
- OperatorUtil:操作工具类
- ExpressionParser:解析表达式,根据规则选择执行的解析器进行解析
- TestMain:测试方法
解释器接口
/**
* 解释器接口
*
* @author shengyong.huang
* @date 2020-07-25
*/
public interface Interpreter {
int interpret();
}
加法解释器和乘法解释器实现
/**
* 加法解释器
*
* @author shengyong.huang
* @date 2020-07-25
*/
public class AddInterpreter implements Interpreter {
private Interpreter firstExpression, secondExpression;
public AddInterpreter(Interpreter firstExpression, Interpreter secondExpression) {
this.firstExpression = firstExpression;
this.secondExpression = secondExpression;
}
@Override
public int interpret() {
return this.firstExpression.interpret() + this.secondExpression.interpret();
}
@Override
public String toString() {
return "+";
}
}
/**
* 乘法解释器
*
* @author shengyong.huang
* @date 2020-07-25
*/
public class MultiInterpreter implements Interpreter {
private Interpreter firstExpression, secondExpression;
public MultiInterpreter(Interpreter firstExpression, Interpreter secondExpression) {
this.firstExpression = firstExpression;
this.secondExpression = secondExpression;
}
@Override
public int interpret() {
return this.firstExpression.interpret() * this.secondExpression.interpret();
}
@Override
public String toString() {
return "*";
}
}
数字转换解释器
/**
* 数字转换解释器
*
* @author shengyong.huang
* @date 2020-07-25
*/
public class NumberInterpreter implements Interpreter {
private int number;
public NumberInterpreter(int number) {
this.number = number;
}
public NumberInterpreter(String number) {
this.number = Integer.parseInt(number);
}
@Override
public int interpret() {
return this.number;
}
}
解析表达式,根据规则选择执行的解析器进行解析
/**
* 解析表达式,根据规则选择执行的解析器进行解析
*
* @author shengyong.huang
* @date 2020-07-25
*/
public class ExpressionParser {
private Stack<Interpreter> stack = new Stack<>();
public int parse(String str) {
String[] stringArray = str.split(" ");
for (String symbol : stringArray) {
if (!OperatorUtil.isOperator(symbol)) {
Interpreter numberExpression = new NumberInterpreter(symbol);
stack.push(numberExpression);
System.out.println(String.format("入栈: %d", numberExpression.interpret()));
} else {
// 运算符合,可以计算
Interpreter first = stack.pop();
Interpreter second = stack.pop();
System.out.println(String.format("出栈: %d 和 %d", first.interpret(), second.interpret()));
Interpreter operator = OperatorUtil.getExpressionObject(first, second, symbol);
System.out.println(String.format("运算符号: %s", operator));
int result = operator.interpret();
NumberInterpreter resultExpression = new NumberInterpreter(result);
stack.push(resultExpression);
System.out.println(String.format("阶段结果入栈: %d", resultExpression.interpret()));
}
}
int result = stack.pop().interpret();
return result;
}
}
操作工具类
/**
* 操作工具类
* @author shengyong.huang
* @date 2020-07-25
*/
public class OperatorUtil {
public static boolean isOperator(String symbol) {
return ("+".equals(symbol) || "*".equals(symbol));
}
public static Interpreter getExpressionObject(Interpreter first, Interpreter second, String symbol) {
if ("+".equals(symbol)) {
return new AddInterpreter(first, second);
} else if ("*".equals(symbol)) {
return new MultiInterpreter(first, second);
} else {
return null;
}
}
}
测试方法
/**
* 测试方法
*
* @author shengyong.huang
* @date 2020-07-25
*/
public class TestMain {
public static void main(String[] args) {
String inputStr = "6 100 11 + *";
ExpressionParser expressionParser = new ExpressionParser();
int result = expressionParser.parse(inputStr);
System.out.println("解释器计算结果: " + result);
}
}
结果展示
入栈: 6
入栈: 100
入栈: 11
出栈: 11 和 100
运算符号: +
阶段结果入栈: 111
出栈: 111 和 6
运算符号: *
阶段结果入栈: 666
解释器计算结果: 666
优点和不足
优点
- 语法由很多类表示,容易改变及扩展此"语言"
缺点
- 可利用场景比较少。
- 对于复杂的文法比较难维护。
- 解释器模式会引起类膨胀。
- 当语法规则数目太多时,增加了系统复杂度
使用场景
- 某个特定类型问题发生频率足够高
- spring中的ExpressionParser,表达式解析器