相信自己,请一定要相信自己
上一章简单介绍了 备忘录模式(二十二), 如果没有看过, 请观看上一章
一. 解释器模式
引用 菜鸟教程里面 解释器模式介绍: https://www.runoob.com/design-pattern/interpreter-pattern.html
解释器模式(Interpreter Pattern)提供了评估语言的语法或表达式的方式,它属于行为型模式。
这种模式实现了一个表达式接口,该接口解释一个特定的上下文。
这种模式被用在 SQL 解析、符号处理引擎等。
一.一 介绍
意图: 给定一个语言,定义它的文法表示,并定义一个解释器,这个解释器使用该标识来解释语言中的句子。
主要解决: 对于一些固定文法构建一个解释句子的解释器。
何时使用: 如果一种特定类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子。
这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题。
如何解决: 构建语法树,定义终结符与非终结符。
关键代码: 构建环境类,包含解释器之外的一些全局信息,一般是 HashMap。
应用实例: 编译器、运算表达式计算。
优点:
1、可扩展性比较好,灵活。
2、增加了新的解释表达式的方式。
3、易于实现简单文法。
缺点:
1、可利用场景比较少。
2、对于复杂的文法比较难维护。
3、解释器模式会引起类膨胀。
4、解释器模式采用递归调用方法。
使用场景:
1、可以将一个需要解释执行的语言中的句子表示为一个抽象语法树。
2、一些重复出现的问题可以用一种简单的语言来进行表达。
3、一个简单语法需要解释的场景。
注意事项: 可利用场景比较少,JAVA 中如果碰到可以用 expression4J 代替。
组成角色 | 具体 | 关系 |
---|---|---|
抽象表达式(AbstractExpression) | MathExpression | 抽象表达式, 声明一个抽象的解释操作,这个方法为抽象语法树中所有的节点所共享 |
终结符表达式(TerminalExpression) | BaseMathExpression | 为终结符表达式, 实现与文法中的终结符相关的解释操作 |
非终结符表达式(NonTermialExpression) | VarExpression | 非终结符表达式,为文法中的非终结符实现解释操作. |
环境角色(Context) | MathCalculator | 环境角色,含有解释器之外的全局信息 |
二. 解释器模式实例
二. 一 抽象表达式 MathExpression
public abstract class MathExpression {
/**
处理表达式
* @param paramMap
*/
public abstract int interpreter (Map<String, Integer> paramMap);
}
二.二 非终结符表达式 VarExpression
public class VarExpression extends MathExpression {
private String key;
public VarExpression (String key) {
this.key = key;
}
@Override
public int interpreter(Map<String, Integer> paramMap) {
return paramMap.get(key);
}
}
二.三 终结符表达式 和其子类
二.三.一 终结符表达式 BaseMathExpression
public class BaseMathExpression extends MathExpression{
private MathExpression left;
private MathExpression right;
public BaseMathExpression (MathExpression left, MathExpression right) {
this.left = left;
this.right = right;
}
@Override
public int interpreter(Map<String, Integer> paramMap) {
// 是个空方法,让子类进行重写.
return 0;
}
public MathExpression getLeft() {
return left;
}
public MathExpression getRight() {
return right;
}
}
二.三.二 终结符表达式子类
相加
public class AddExpression extends BaseMathExpression{
public AddExpression(MathExpression left, MathExpression right) {
super(left, right);
}
@Override
public int interpreter(Map<String, Integer> paramMap) {
return super.getLeft().interpreter(paramMap) + super.getRight().interpreter(paramMap);
}
}
相减
public class SubExpression extends BaseMathExpression{
public SubExpression(MathExpression left, MathExpression right) {
super(left, right);
}
@Override
public int interpreter(Map<String, Integer> paramMap) {
return super.getLeft().interpreter(paramMap) - super.getRight().interpreter(paramMap);
}
}
二.四 环境角色 MathCalculator
public class MathCalculator {
private MathExpression mathExpression;
/**
构造方法传入一个表达式
*/
public MathCalculator (String expStr) {
// 安排运算先后顺序
Stack<MathExpression> stack = new Stack<>();
// 表达式拆分成字符数组
// 进行拆分
char[] charArray = expStr.toCharArray();
MathExpression left = null;
MathExpression right = null;
//遍历我们的字符数组, 即遍历 [a, +, b]
//针对不同的情况,做处理
for (int i = 0; i < charArray.length; i++) {
switch (charArray[i]) {
case '+':{
left = stack.pop();// 从stack取出left => "a"
right = new VarExpression(String.valueOf(charArray[++i]));// 取出右表达式 "b"
stack.push(new AddExpression(left, right));// 然后根据得到left 和 right 构建 AddExpresson加入stack
break;
}
case '-':{
left = stack.pop();
right = new VarExpression(String.valueOf(charArray[++i]));
stack.push(new SubExpression(left, right));
break;
}
default:{
//如果是一个 Var 就创建要给 VarMathExpression 对象,并push到 stack
stack.push(new VarExpression(String.valueOf(charArray[i])));
break;
}
}
}
//当遍历完整个 charArray 数组后,stack 就得到最后MathExpression
this.mathExpression = stack.pop();
}
/**
对外提供的一个方法, 用于获取结果
*/
public int exec (Map<String,Integer> paramMap) {
return this.mathExpression.interpreter(paramMap);
}
}
二.五 客户端调用
@Test
public void oneTest() {
MathCalculator mathCalculator = new MathCalculator("a+b-c+d-e");
// 定义参数
Map<String,Integer> paramMap = new HashMap<>();
paramMap.put("a",1);
paramMap.put("b",2);
paramMap.put("c",3);
paramMap.put("d",4);
paramMap.put("e",5);
int result = mathCalculator.exec(paramMap);
log.info("最后结果是:{}",result);
}
INFO [main] (InterpreterTest.java:33) - 最后结果是:-1
三. JEP 处理数学表达式
三.一 引入jep 依赖
<dependency>
<groupId>jep</groupId>
<artifactId>jep</artifactId>
<version>2.24</version>
</dependency>
三.二 JEP使用测试
@Test
public void twoTest() {
JEP jep = new JEP();
// 需要先添加变量, 再处理表达式。
jep.addVariable("a",1);
jep.addVariable("b",2);
jep.addVariable("c",3);
jep.addVariable("d",4);
jep.addVariable("e",5);
jep.parseExpression("a+b-c+d-e");
// 进行获取结果
log.info(">>> 最后结果: {}", jep.getValue());
}
INFO [main] (InterpreterTest.java:48) - >>> 最后结果: -1.0
较复杂的公式可以使用 SpEL 处理。 SpEL 可以看老蝴蝶之前写的文章: SpringBoot SpEL表达式(五十二)
本章节的代码放置在 github 上:
https://github.com/yuejianli/DesignPattern/tree/develop/Interpreter
谢谢您的观看,如果喜欢,请关注我,再次感谢 !!!