一次解释器模式的实际使用
先说我的需求
给定下面一个表达式字符串
0;1;[2,4);[4,6);[6,12);>=12
在给定一个数值,例如 5
要求,找出满足5 的表达式,例如5 应该输出 [4,6)
我先把数据拆分下,如上所示,每个表达式都用的分号;隔开
于是就拆分成了一个列表
0
1
[2,4)
开始实现功能,以下为第一次实现,没有使用解释器模式
import java.util.List;
/**
* 范围区间值策略
* 示例 <=25;(25,35];[35,45);[45,55);[55,60);[60,65);>=65
* 强制!必须由小到大排序,必须是此格式,否则该接口无法正常工作
*
* @author SUN
* @date 04/09/2022
*/
public class RangeValuePolicy implements ValuePolicy {
private static final String LEFT_OPEN_RANGE_SYMBOL = "[";
private static final String RIGHT_OPEN_RANGE_SYMBOL = "]";
private static final String LEFT_CLOSE_RANGE_SYMBOL = "(";
private static final String RIGHT_CLOSE_RANGE_SYMBOL = ")";
private static final String EQUALS_SYMBOL = "=";
private static final String LESS_THAN_SYMBOL = "<";
private static final String GRANT_THAN_SYMBOL = ">";
private static final String COMMA_SYMBOL = ",";
@SuppressWarnings("all")
@Override
public String compute(Object value, List<String> compareValue) {
long longValue;
try {
longValue = Long.parseLong(String.valueOf(value));
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("解析出错!参数不是数值类型:" + value);
}
for (String express : compareValue) {
boolean startWithLt = express.startsWith(LESS_THAN_SYMBOL);
boolean startWithGt = express.startsWith(GRANT_THAN_SYMBOL);
if (startWithLt || startWithGt) {
int equals = express.indexOf(EQUALS_SYMBOL);
if (equals != -1) {
// 存在
// 获取小于等于的数字
long expressValue = Long.parseLong(express.substring(2));
if (startWithLt) {
if (longValue <= expressValue) {
return express;
}
} else {
if (longValue >= expressValue) {
return express;
}
}
} else {
// 获取小于的数字
long expressValue = Long.parseLong(express.substring(1));
if (startWithLt) {
if (longValue < expressValue) {
return express;
}
} else {
if (longValue > expressValue) {
return express;
}
}
}
continue;
}
// 区间范围判断
String[] compareArray = express.split(COMMA_SYMBOL);
if (compareArray.length == 2) {
String left = compareArray[0];
String right = compareArray[1];
// 左区间符号
String leftRangeSymbol = left.substring(0, 1);
// 左区间数值
long leftRangeValue = Long.parseLong(left.substring(1));
// 右区间符号
String rightRangeSymbol = right.substring(right.length() - 1);
// 右区间数值
long rightRangeValue = Long.parseLong(right.substring(0, right.length() - 1));
// 判断是否满足条件
if (
// 值大于等于 开区间值
(LEFT_OPEN_RANGE_SYMBOL.equals(leftRangeSymbol) && longValue >= leftRangeValue)
// 或者 值大于闭区间值
|| LEFT_CLOSE_RANGE_SYMBOL.equals(leftRangeSymbol) && longValue > leftRangeValue) {
if (
// 值小于等于 开区间值
(RIGHT_OPEN_RANGE_SYMBOL.equals(rightRangeSymbol) && longValue <= rightRangeValue)
// 或者值小于闭区间值
|| RIGHT_CLOSE_RANGE_SYMBOL.equals(rightRangeSymbol) && longValue < rightRangeValue) {
// 满足表达式
return express;
}
}
} else {
// 单值判断
Long expressLongValue = Long.valueOf(express);
if (expressLongValue.equals(longValue)) {
return express;
}
continue;
}
}
return null;
}
}
这段代码看起来... 很直观,简单,但是却不易维护,没有扩展性。
解释器模式刚好可以实现这个功能,学起来还是有点痛苦,以至于我第一次看时完全无法理解。不过好在,我找到一本书,叫做《秒懂设计模式》,终于理解了。
阅读链接 秒懂设计模式-刘韬-微信读书 (qq.com)
学会了解释器模式,现在开始改造代码,首当其冲的难点在于如何定义表达式,如何定义终结表达式与非终结表达式。
好的,首先画一幅只有自己能够看懂的图,来帮助自己梳理逻辑
0;1;[2,4);[4,6);[6,12);>=12
对于给定表达式,一个个来看,首先第一种 0 和 1
这种的判断就是 == 直接等于,也就是等于
再看第二种 >= 这种是可以拆分的,也就是 大于 和 等于 两个条件
此时,结构变成了如此
区间如何表示呢?[1,3)
其实可以发现,区间属于复合运算,就是左区间 和 右区间 的运算
将区间拆开 分为左区间 和 右区间
即 [1 3)
一个个分析,对于这半个区间,特点就是一个符号 + 一个数值
符号代表了判断的规则,数值代表比较的内容
对于[1 左闭合区间 可以拆分为 >1
对于(1 左闭合区间 可以拆分为 >=1
右闭合区间同理
而拆分后的结果又在上图的语法树上,证明是可以用的
由于区间是两个表达式,所以得新建一个表达式类型
区间(1,3)就如下结构表示
闭区间如下表示 (紫色粗线只为看起来醒目些,无其他意义)
结构可以了,看看代码最终实现吧
git地址 https://gitee.com/sixsixsix516/design-mode-interpreter
package com.example;
/**
* 解释器接口
* @author SUN
* @date 2022/9/7
*/
public abstract class AbstractExpression {
/**
* 当前表达式字符串
*/
protected String express;
/**
* 待比较的值
*/
protected long compareValue;
/**
* 解释方法
* @return true符合条件 false不符合条件
*/
public abstract boolean interpret(long value);
/**
* 从表达式中解析出要比较的value
*/
protected long parseExpressCompareValue(String compareValueExpress) {
return 0;
}
}
package com.example.ultimate;
import com.example.AbstractExpression;
/**
* 等于
*
* @author SUN
* @date 2022/9/7
*/
public class EqualExpress extends AbstractExpression {
public EqualExpress(long compareValue) {
super.compareValue = compareValue;
}
@Override
public boolean interpret(long value) {
return super.compareValue == value;
}
}
package com.example.ultimate;
import com.example.AbstractExpression;
import com.example.constant.ExpressConstant;
/**
* 大于等于
*
* @author SUN
* @date 2022/9/7
*/
public class GreaterThanEqualExpress extends AbstractExpression {
/**
* 大于
*/
private final GreaterThanExpress greaterThanExpress;
/**
* 等于
*/
private final EqualExpress equalExpress;
public GreaterThanEqualExpress(String compareValueExpress) {
super.express = compareValueExpress;
long compareValue = parseExpressCompareValue(compareValueExpress);
this.greaterThanExpress = new GreaterThanExpress(compareValue);
this.equalExpress = new EqualExpress(compareValue);
}
public GreaterThanEqualExpress(long compareValue) {
this.greaterThanExpress = new GreaterThanExpress(compareValue);
this.equalExpress = new EqualExpress(compareValue);
}
@Override
public boolean interpret(long value) {
return greaterThanExpress.interpret(value) || equalExpress.interpret(value);
}
/**
* 解析格式 >=10
*/
@Override
protected long parseExpressCompareValue(String compareValueExpress) {
return Long.parseLong(compareValueExpress.substring(ExpressConstant.GRANT_THAN_EQUALS_SYMBOL.length()));
}
}
package com.example;
import com.example.ultimate.*;
import java.util.ArrayList;
import java.util.List;
import static com.example.constant.ExpressConstant.*;
/**
* @author SUN
* @date 2022/9/7
*/
public class RangeValuePolicy {
/**
* 表达式列表、语法树
*/
private List<AbstractExpression> abstractExpressionList;
public String interpret(long value) {
for (AbstractExpression abstractExpression : abstractExpressionList) {
boolean interpret = abstractExpression.interpret(value);
if (interpret) {
return abstractExpression.express;
}
}
return null;
}
/**
* 语法解析
*
* @param expressList <10; <=0;[2,4);[4,6);[6,12);>=12 以分号分隔的列表
*/
public void parse(List<String> expressList) {
abstractExpressionList = new ArrayList<>();
for (String express : expressList) {
if (express.startsWith(LESS_THAN_EQUALS_SYMBOL)) {
abstractExpressionList.add(new LessThanEqualExpress(express));
} else if (express.startsWith(LESS_THAN_SYMBOL)) {
abstractExpressionList.add(new LessThanExpress(express));
} else if (express.startsWith(GRANT_THAN_EQUALS_SYMBOL)) {
abstractExpressionList.add(new GreaterThanEqualExpress(express));
} else if (express.startsWith(GRANT_THAN_SYMBOL)) {
abstractExpressionList.add(new GreaterThanExpress(express));
} else if (express.startsWith(LEFT_CLOSE_RANGE_SYMBOL) || express.startsWith(LEFT_OPEN_RANGE_SYMBOL)) {
// 区间
abstractExpressionList.add(parseRangeExpress(express));
}
}
}
/**
* 解析区间表达式
*/
private RangeExpress parseRangeExpress(String express) {
String[] rangeArray = express.split(",");
// 左区间表达式 格式 (1 或 [16
String leftExpressSymbol = rangeArray[0];
// 左区间符号 ( [
String leftSymbol = leftExpressSymbol.substring(0, 1);
// 左区间数值
long leftValue = Long.parseLong(leftExpressSymbol.substring(1));
AbstractExpression leftExpress;
if (leftSymbol.equals(LEFT_OPEN_RANGE_SYMBOL)) {
leftExpress = new GreaterThanExpress(leftValue);
} else {
leftExpress = new GreaterThanExpress(leftValue);
}
// 右区间表达式
String rightExpressSymbol = rangeArray[1];
// 右区间符号 ) ]
String rightRangeSymbol = rightExpressSymbol.substring(rightExpressSymbol.length() - 1);
// 右区间数值
long rightRangeValue = Long.parseLong(rightExpressSymbol.substring(0, rightExpressSymbol.length() - 1));
AbstractExpression rightExpress;
if (rightRangeSymbol.equals(RIGHT_OPEN_RANGE_SYMBOL)) {
rightExpress = new LessThanEqualExpress(rightRangeValue);
} else {
rightExpress = new LessThanExpress(rightRangeValue);
}
return new RangeExpress(leftExpress, rightExpress, express);
}
}
代码就随便粘几个,完整的仓库下载后自己看
git地址 https://gitee.com/sixsixsix516/design-mode-interpreter