• 一次解释器模式的实际使用


    先说我的需求

    给定下面一个表达式字符串

    0;1;[2,4);[4,6);[6,12);>=12

    在给定一个数值,例如 5

    要求,找出满足5 的表达式,例如5 应该输出 [4,6)

    我先把数据拆分下,如上所示,每个表达式都用的分号;隔开

    于是就拆分成了一个列表

    0

    1

    [2,4)

    开始实现功能,以下为第一次实现,没有使用解释器模式

    1. import java.util.List;
    2. /**
    3. * 范围区间值策略
    4. * 示例 <=25;(25,35];[35,45);[45,55);[55,60);[60,65);>=65
    5. * 强制!必须由小到大排序,必须是此格式,否则该接口无法正常工作
    6. *
    7. * @author SUN
    8. * @date 04/09/2022
    9. */
    10. public class RangeValuePolicy implements ValuePolicy {
    11. private static final String LEFT_OPEN_RANGE_SYMBOL = "[";
    12. private static final String RIGHT_OPEN_RANGE_SYMBOL = "]";
    13. private static final String LEFT_CLOSE_RANGE_SYMBOL = "(";
    14. private static final String RIGHT_CLOSE_RANGE_SYMBOL = ")";
    15. private static final String EQUALS_SYMBOL = "=";
    16. private static final String LESS_THAN_SYMBOL = "<";
    17. private static final String GRANT_THAN_SYMBOL = ">";
    18. private static final String COMMA_SYMBOL = ",";
    19. @SuppressWarnings("all")
    20. @Override
    21. public String compute(Object value, List compareValue) {
    22. long longValue;
    23. try {
    24. longValue = Long.parseLong(String.valueOf(value));
    25. } catch (Exception e) {
    26. e.printStackTrace();
    27. throw new RuntimeException("解析出错!参数不是数值类型:" + value);
    28. }
    29. for (String express : compareValue) {
    30. boolean startWithLt = express.startsWith(LESS_THAN_SYMBOL);
    31. boolean startWithGt = express.startsWith(GRANT_THAN_SYMBOL);
    32. if (startWithLt || startWithGt) {
    33. int equals = express.indexOf(EQUALS_SYMBOL);
    34. if (equals != -1) {
    35. // 存在
    36. // 获取小于等于的数字
    37. long expressValue = Long.parseLong(express.substring(2));
    38. if (startWithLt) {
    39. if (longValue <= expressValue) {
    40. return express;
    41. }
    42. } else {
    43. if (longValue >= expressValue) {
    44. return express;
    45. }
    46. }
    47. } else {
    48. // 获取小于的数字
    49. long expressValue = Long.parseLong(express.substring(1));
    50. if (startWithLt) {
    51. if (longValue < expressValue) {
    52. return express;
    53. }
    54. } else {
    55. if (longValue > expressValue) {
    56. return express;
    57. }
    58. }
    59. }
    60. continue;
    61. }
    62. // 区间范围判断
    63. String[] compareArray = express.split(COMMA_SYMBOL);
    64. if (compareArray.length == 2) {
    65. String left = compareArray[0];
    66. String right = compareArray[1];
    67. // 左区间符号
    68. String leftRangeSymbol = left.substring(0, 1);
    69. // 左区间数值
    70. long leftRangeValue = Long.parseLong(left.substring(1));
    71. // 右区间符号
    72. String rightRangeSymbol = right.substring(right.length() - 1);
    73. // 右区间数值
    74. long rightRangeValue = Long.parseLong(right.substring(0, right.length() - 1));
    75. // 判断是否满足条件
    76. if (
    77. // 值大于等于 开区间值
    78. (LEFT_OPEN_RANGE_SYMBOL.equals(leftRangeSymbol) && longValue >= leftRangeValue)
    79. // 或者 值大于闭区间值
    80. || LEFT_CLOSE_RANGE_SYMBOL.equals(leftRangeSymbol) && longValue > leftRangeValue) {
    81. if (
    82. // 值小于等于 开区间值
    83. (RIGHT_OPEN_RANGE_SYMBOL.equals(rightRangeSymbol) && longValue <= rightRangeValue)
    84. // 或者值小于闭区间值
    85. || RIGHT_CLOSE_RANGE_SYMBOL.equals(rightRangeSymbol) && longValue < rightRangeValue) {
    86. // 满足表达式
    87. return express;
    88. }
    89. }
    90. } else {
    91. // 单值判断
    92. Long expressLongValue = Long.valueOf(express);
    93. if (expressLongValue.equals(longValue)) {
    94. return express;
    95. }
    96. continue;
    97. }
    98. }
    99. return null;
    100. }
    101. }

    这段代码看起来... 很直观,简单,但是却不易维护,没有扩展性。

    解释器模式刚好可以实现这个功能,学起来还是有点痛苦,以至于我第一次看时完全无法理解。不过好在,我找到一本书,叫做《秒懂设计模式》,终于理解了。

    阅读链接 秒懂设计模式-刘韬-微信读书 (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

    1. package com.example;
    2. /**
    3. * 解释器接口
    4. * @author SUN
    5. * @date 2022/9/7
    6. */
    7. public abstract class AbstractExpression {
    8. /**
    9. * 当前表达式字符串
    10. */
    11. protected String express;
    12. /**
    13. * 待比较的值
    14. */
    15. protected long compareValue;
    16. /**
    17. * 解释方法
    18. * @return true符合条件 false不符合条件
    19. */
    20. public abstract boolean interpret(long value);
    21. /**
    22. * 从表达式中解析出要比较的value
    23. */
    24. protected long parseExpressCompareValue(String compareValueExpress) {
    25. return 0;
    26. }
    27. }
    1. package com.example.ultimate;
    2. import com.example.AbstractExpression;
    3. /**
    4. * 等于
    5. *
    6. * @author SUN
    7. * @date 2022/9/7
    8. */
    9. public class EqualExpress extends AbstractExpression {
    10. public EqualExpress(long compareValue) {
    11. super.compareValue = compareValue;
    12. }
    13. @Override
    14. public boolean interpret(long value) {
    15. return super.compareValue == value;
    16. }
    17. }
    1. package com.example.ultimate;
    2. import com.example.AbstractExpression;
    3. import com.example.constant.ExpressConstant;
    4. /**
    5. * 大于等于
    6. *
    7. * @author SUN
    8. * @date 2022/9/7
    9. */
    10. public class GreaterThanEqualExpress extends AbstractExpression {
    11. /**
    12. * 大于
    13. */
    14. private final GreaterThanExpress greaterThanExpress;
    15. /**
    16. * 等于
    17. */
    18. private final EqualExpress equalExpress;
    19. public GreaterThanEqualExpress(String compareValueExpress) {
    20. super.express = compareValueExpress;
    21. long compareValue = parseExpressCompareValue(compareValueExpress);
    22. this.greaterThanExpress = new GreaterThanExpress(compareValue);
    23. this.equalExpress = new EqualExpress(compareValue);
    24. }
    25. public GreaterThanEqualExpress(long compareValue) {
    26. this.greaterThanExpress = new GreaterThanExpress(compareValue);
    27. this.equalExpress = new EqualExpress(compareValue);
    28. }
    29. @Override
    30. public boolean interpret(long value) {
    31. return greaterThanExpress.interpret(value) || equalExpress.interpret(value);
    32. }
    33. /**
    34. * 解析格式 >=10
    35. */
    36. @Override
    37. protected long parseExpressCompareValue(String compareValueExpress) {
    38. return Long.parseLong(compareValueExpress.substring(ExpressConstant.GRANT_THAN_EQUALS_SYMBOL.length()));
    39. }
    40. }
    1. package com.example;
    2. import com.example.ultimate.*;
    3. import java.util.ArrayList;
    4. import java.util.List;
    5. import static com.example.constant.ExpressConstant.*;
    6. /**
    7. * @author SUN
    8. * @date 2022/9/7
    9. */
    10. public class RangeValuePolicy {
    11. /**
    12. * 表达式列表、语法树
    13. */
    14. private List abstractExpressionList;
    15. public String interpret(long value) {
    16. for (AbstractExpression abstractExpression : abstractExpressionList) {
    17. boolean interpret = abstractExpression.interpret(value);
    18. if (interpret) {
    19. return abstractExpression.express;
    20. }
    21. }
    22. return null;
    23. }
    24. /**
    25. * 语法解析
    26. *
    27. * @param expressList <10; <=0;[2,4);[4,6);[6,12);>=12 以分号分隔的列表
    28. */
    29. public void parse(List expressList) {
    30. abstractExpressionList = new ArrayList<>();
    31. for (String express : expressList) {
    32. if (express.startsWith(LESS_THAN_EQUALS_SYMBOL)) {
    33. abstractExpressionList.add(new LessThanEqualExpress(express));
    34. } else if (express.startsWith(LESS_THAN_SYMBOL)) {
    35. abstractExpressionList.add(new LessThanExpress(express));
    36. } else if (express.startsWith(GRANT_THAN_EQUALS_SYMBOL)) {
    37. abstractExpressionList.add(new GreaterThanEqualExpress(express));
    38. } else if (express.startsWith(GRANT_THAN_SYMBOL)) {
    39. abstractExpressionList.add(new GreaterThanExpress(express));
    40. } else if (express.startsWith(LEFT_CLOSE_RANGE_SYMBOL) || express.startsWith(LEFT_OPEN_RANGE_SYMBOL)) {
    41. // 区间
    42. abstractExpressionList.add(parseRangeExpress(express));
    43. }
    44. }
    45. }
    46. /**
    47. * 解析区间表达式
    48. */
    49. private RangeExpress parseRangeExpress(String express) {
    50. String[] rangeArray = express.split(",");
    51. // 左区间表达式 格式 (1 或 [16
    52. String leftExpressSymbol = rangeArray[0];
    53. // 左区间符号 ( [
    54. String leftSymbol = leftExpressSymbol.substring(0, 1);
    55. // 左区间数值
    56. long leftValue = Long.parseLong(leftExpressSymbol.substring(1));
    57. AbstractExpression leftExpress;
    58. if (leftSymbol.equals(LEFT_OPEN_RANGE_SYMBOL)) {
    59. leftExpress = new GreaterThanExpress(leftValue);
    60. } else {
    61. leftExpress = new GreaterThanExpress(leftValue);
    62. }
    63. // 右区间表达式
    64. String rightExpressSymbol = rangeArray[1];
    65. // 右区间符号 ) ]
    66. String rightRangeSymbol = rightExpressSymbol.substring(rightExpressSymbol.length() - 1);
    67. // 右区间数值
    68. long rightRangeValue = Long.parseLong(rightExpressSymbol.substring(0, rightExpressSymbol.length() - 1));
    69. AbstractExpression rightExpress;
    70. if (rightRangeSymbol.equals(RIGHT_OPEN_RANGE_SYMBOL)) {
    71. rightExpress = new LessThanEqualExpress(rightRangeValue);
    72. } else {
    73. rightExpress = new LessThanExpress(rightRangeValue);
    74. }
    75. return new RangeExpress(leftExpress, rightExpress, express);
    76. }
    77. }

    代码就随便粘几个,完整的仓库下载后自己看

     git地址 https://gitee.com/sixsixsix516/design-mode-interpreter

  • 相关阅读:
    ChatGPT AIGC 完成超炫酷的大屏可视化
    day062:平衡二叉树——左旋、右旋
    PingCode DevOps 团队:企业CICD流水线可能会遇到的问题及解法
    UVA524 素数环 Prime Ring Problem
    产品经理-战略-战略的含义和层级
    SpringBoot读取配置的方式
    解析分布式数据库的技术框架及其在金融行业中的应用规划
    oak深度相机入门教程-累积行人计数
    多个服务器的用户共享同一个用户目录的做法
    如何防止API接口被未授权的客户端调用?
  • 原文地址:https://blog.csdn.net/weixin_42195284/article/details/126746122