先说我的需求
给定下面一个表达式字符串
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
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;
- }
- }
这段代码看起来... 很直观,简单,但是却不易维护,没有扩展性。
解释器模式刚好可以实现这个功能,学起来还是有点痛苦,以至于我第一次看时完全无法理解。不过好在,我找到一本书,叫做《秒懂设计模式》,终于理解了。
学会了解释器模式,现在开始改造代码,首当其冲的难点在于如何定义表达式,如何定义终结表达式与非终结表达式。
好的,首先画一幅只有自己能够看懂的图,来帮助自己梳理逻辑

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
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
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