• 【再探】设计模式—备忘录模式与解释器模式


     备忘录模式是用于保存对象在某个时刻的状态,来实现撤销操作。而解释器模式则是将文本按照定义的文法规则解析成对应的命令。

    1 备忘录模式

    需求:保存对象在某个时刻的状态,后面可以对该对象实行撤销操作。

    1.1 备忘录模式介绍

    提供一种状态恢复机制,在不破坏封装的前提下,捕获对象内部状态并在该对象之外保存这个状态。可以在以后将对象恢复到原先保存的状态。

    图 备忘录模式 UML

    1. public class MementoPattern {
    2. public static void main(String[] args) {
    3. String[] colors = {"red","blue","green","pink","black"};
    4. Button button = new Button();
    5. Random random = new Random();
    6. MementoCareTaker<ButtonMemento> careTaker = new MementoCareTaker<>();
    7. for (int i = 0; i < 10; i++) {
    8. button.setColor(colors[random.nextInt(colors.length)]);
    9. button.setPositionX(random.nextDouble());
    10. careTaker.pushMemento(button.createMemento());
    11. }
    12. button.restoreFromMemento(careTaker.getMemento(2));
    13. System.out.println(button);
    14. button.restoreFromMemento(careTaker.getMemento(3));
    15. System.out.println(button);
    16. button.restoreFromMemento(careTaker.getMemento(1));
    17. System.out.println(button);
    18. }
    19. private static class Button {
    20. private String color;
    21. private Double positionX;
    22. public String getColor() {
    23. return color;
    24. }
    25. public void setColor(String color) {
    26. this.color = color;
    27. }
    28. public Double getPositionX() {
    29. return positionX;
    30. }
    31. public void setPositionX(Double positionX) {
    32. this.positionX = positionX;
    33. }
    34. public ButtonMemento createMemento() {
    35. return new ButtonMemento(color,positionX);
    36. }
    37. public void restoreFromMemento(ButtonMemento memento) {
    38. this.color = memento.getColor();
    39. this.positionX = memento.positionX;;
    40. }
    41. @Override
    42. public String toString() {
    43. return "Button{" +
    44. "color='" + color + '\'' +
    45. ", positionX=" + positionX +
    46. '}';
    47. }
    48. }
    49. private static class ButtonMemento {
    50. private final String color;
    51. private final Double positionX;
    52. public ButtonMemento(String color, Double positionX) {
    53. this.color = color;
    54. this.positionX = positionX;
    55. }
    56. public String getColor() {
    57. return color;
    58. }
    59. public Double getPositionX() {
    60. return positionX;
    61. }
    62. }
    63. private static class MementoCareTaker {
    64. private final Stack stack = new Stack<>();
    65. public void pushMemento(T t) {
    66. stack.push(t);
    67. }
    68. public T getMemento(int num) {
    69. T t = null;
    70. while (num-- > 0 && !stack.isEmpty()) {
    71. t = stack.pop();
    72. }
    73. return t;
    74. }
    75. }
    76. }

    1.2 优缺点

    优点:

    1. 提供了一种状态恢复机制,对象可以方便地回到一个特定的历史步骤状态。

    缺点:

    1. 需要保存不同时刻的对象状态,这将耗费许多内存等资源。
    2. 类的数量增多,当对象自带有变动时,对应的备忘类也需要修改。

    2 解释器模式

    需求:将文本按照特定的语法规则转换成计算机中特定的命令。

    2.1 解释器模式介绍

    定义一个语言的文法,并建立一个解释器来解释该语言中的句子。这里的“语言”是指使用特定格式和语法的代码。

    图 解释器UML

    这里的Context 一般用于存储解释器之外的一些全局信息,也可以省略这个类。

    ::=

    定义为。

    |

    或。

     ‘{’和‘}’

    组合。

    *

    出现0或多次。

    语言单位

    语言构造成分,每一条语句所定义的字符串。

    终结表达式

    组成元素是最基本的语言单位,不能在进行分解。

    非终结表达式

    组成元素仍可以是表达式,可进一步分解。

    图 文法规则说明

    1. public class InterpreterPattern {
    2. /**
    3. * 语法规则:
    4. * value ::= a integer
    5. * operator ::= '+' | '-' | '*' | '/'
    6. * expression ::= value operator value | complexExpression
    7. * complexExpression ::= expression operator expression | expression operator (expression)
    8. */
    9. public static void main(String[] args) {
    10. String[] textArr = {"1+4*3","4*5+3","(3+4)*23", "(3+24)-(23-8)", "(2-3)*(24+2*3)", "(((1+2)*(2+3)))+32"};
    11. for (int i = 0; i < textArr.length; i++) {
    12. System.out.print(textArr[i]);
    13. System.out.println("=" + new ComplexExpression().interpreter(textArr[i]));
    14. }
    15. }
    16. private static abstract class Expression {
    17. abstract int interpreter(String text);
    18. protected int compute(int leftNum,int rightNum,char opera) {
    19. switch (opera) {
    20. case '+': return leftNum + rightNum;
    21. case '-': return leftNum - rightNum;
    22. case '*': return leftNum * rightNum;
    23. case '/': return leftNum / rightNum;
    24. }
    25. return 0;
    26. }
    27. }
    28. /**
    29. * 复杂表达式
    30. */
    31. private static class ComplexExpression extends Expression {
    32. @Override
    33. int interpreter(String text) {
    34. // System.out.println("ComplexExpression:" + text);
    35. int letNum=0;
    36. int rightNum=0;
    37. char opera = ' ';
    38. Pattern pattern = Pattern.compile("[\\+\\-\\*\\/]");
    39. Matcher matcher = pattern.matcher(text);
    40. if (matcher.find()) {
    41. int start = matcher.start();
    42. opera = text.charAt(start);
    43. if (text.indexOf("(") == 0) { // 在操作符前面有括号, 先计算括号的内容
    44. int endBracketPos = findEndBracketPos(text);
    45. letNum = new ComplexExpression().interpreter(text.substring(1,endBracketPos));
    46. if (endBracketPos == text.length() -1 ) {
    47. return letNum;
    48. }
    49. opera = text.charAt(endBracketPos+1);
    50. rightNum = new ComplexExpression().interpreter(text.substring(endBracketPos+2));
    51. } else if ((opera == '*' || opera == '/') && text.charAt(start+1) != '(') { // 需要先完成左边运算
    52. boolean hasNext = matcher.find(start + 1);
    53. if (hasNext) {
    54. int pos2 = matcher.start();
    55. letNum = new ComplexExpression().interpreter(text.substring(0,pos2));
    56. opera = text.charAt(pos2);
    57. rightNum = new ComplexExpression().interpreter(text.substring(pos2+1));
    58. } else {
    59. letNum = new TerminalExpression().interpreter(text.substring(0,start));
    60. rightNum = new ComplexExpression().interpreter(text.substring(start+1));
    61. }
    62. } else {
    63. letNum = new TerminalExpression().interpreter(text.substring(0,start));
    64. rightNum = new ComplexExpression().interpreter(text.substring(start+1));
    65. }
    66. return compute(letNum,rightNum,opera);
    67. } else { // 终结表达式
    68. return new TerminalExpression().interpreter(text);
    69. }
    70. }
    71. private int findEndBracketPos(String text) {
    72. int startPos = 0,endPos = 0;
    73. do {
    74. endPos = text.indexOf(")",endPos+1);
    75. startPos = text.indexOf("(",startPos+1);
    76. } while (startPos < endPos && startPos > 0);
    77. return endPos;
    78. }
    79. }
    80. /**
    81. * 是一个数值 或者 是 (数值) 形式,要把text 转换为数值
    82. */
    83. private static class TerminalExpression extends Expression {
    84. @Override
    85. int interpreter(String text) {
    86. // System.out.println("TerminalExpression:" + text);
    87. if (text.indexOf("(") == 0) {
    88. text = text.substring(1,text.length() - 1);
    89. }
    90. return Integer.parseInt(text);
    91. }
    92. }
    93. }

    2.2 优缺点

    优点:

    1. 实现文法较为容易。易于改变和扩展文法。增加新的解释表达式较为方便,只需增加相关表达式类即可,符合开闭原则。

    缺点:

    1. 执行效率较低,使用了大量的循环和递归调用,调试过程比较麻烦。
    2. 复杂文法难以维护。如果一种语言包含太多文法规则,类的数量将会急剧增加,导致系统难以管理和维护。
  • 相关阅读:
    【Python百日进阶-数据分析】Day122 - Plotly Figure参数: 散点图(四)
    超全selenium元素定位XPath、CSS
    企业电子杂志如何制作与分享
    从入门到进阶 之 ElasticSearch SpringData 继承篇
    在线问诊 Python、FastAPI、Neo4j — 创建 检查节点
    AD9371 官方例程裸机SW 和 HDL配置概述(三)
    看完通辽可汗小约翰之后应该掌握的英语词汇 01 外交类
    腾讯视频共享设备ip会不会出现错误
    Alibaba Nacos 客户端注册从客户端项目到nacos项目的整体流程核心梳理
    Acwing 3306.装珠饰(十一届蓝桥java/py组J题)
  • 原文地址:https://blog.csdn.net/qq_25308331/article/details/139400390