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

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

1 备忘录模式

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

1.1 备忘录模式介绍

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

图 备忘录模式 UML

public class MementoPattern {

    public static void main(String[] args) {
        String[] colors = {"red","blue","green","pink","black"};
        Button button = new Button();
        Random random = new Random();
        MementoCareTaker<ButtonMemento> careTaker = new MementoCareTaker<>();
        for (int i = 0; i < 10; i++) {
            button.setColor(colors[random.nextInt(colors.length)]);
            button.setPositionX(random.nextDouble());
            careTaker.pushMemento(button.createMemento());
        }
        button.restoreFromMemento(careTaker.getMemento(2));
        System.out.println(button);
        button.restoreFromMemento(careTaker.getMemento(3));
        System.out.println(button);
        button.restoreFromMemento(careTaker.getMemento(1));
        System.out.println(button);
    }

    private static class Button {
        private String color;
        private Double positionX;

        public String getColor() {
            return color;
        }

        public void setColor(String color) {
            this.color = color;
        }

        public Double getPositionX() {
            return positionX;
        }

        public void setPositionX(Double positionX) {
            this.positionX = positionX;
        }

        public ButtonMemento createMemento() {
            return new ButtonMemento(color,positionX);
        }

        public void restoreFromMemento(ButtonMemento memento) {
            this.color = memento.getColor();
            this.positionX = memento.positionX;;
        }

        @Override
        public String toString() {
            return "Button{" +
                    "color='" + color + '\'' +
                    ", positionX=" + positionX +
                    '}';
        }
    }

    private static class ButtonMemento {
        private final String color;
        private final Double positionX;

        public ButtonMemento(String color, Double positionX) {
            this.color = color;
            this.positionX = positionX;
        }

        public String getColor() {
            return color;
        }

        public Double getPositionX() {
            return positionX;
        }

    }

    private static class MementoCareTaker<T> {

        private final Stack<T> stack = new Stack<>();

        public void pushMemento(T t) {
            stack.push(t);
        }

        public T getMemento(int num) {
            T t = null;
            while (num-- > 0 && !stack.isEmpty()) {
                t = stack.pop();
            }
            return t;
        }

    }

}

1.2 优缺点

优点:

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

缺点:

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

2 解释器模式

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

2.1 解释器模式介绍

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

图 解释器UML

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

::=

定义为。

|

或。

 ‘{’和‘}’

组合。

*

出现0或多次。

语言单位

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

终结表达式

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

非终结表达式

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

图 文法规则说明

public class InterpreterPattern {
    /**
     * 语法规则:
     * value ::= a integer
     * operator ::= '+' | '-' | '*' | '/'
     * expression ::= value operator value | complexExpression
     * complexExpression ::= expression operator expression | expression operator (expression)
     */

    public static void main(String[] args) {
        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"};
        for (int i = 0; i < textArr.length; i++) {
            System.out.print(textArr[i]);
            System.out.println("=" + new ComplexExpression().interpreter(textArr[i]));
        }
    }

    private static abstract class Expression {
        abstract int interpreter(String text);

        protected int compute(int leftNum,int rightNum,char opera) {
            switch (opera) {
                case '+': return leftNum + rightNum;
                case '-': return leftNum - rightNum;
                case '*': return leftNum * rightNum;
                case '/': return leftNum / rightNum;
            }
            return 0;
        }
    }

    /**
     * 复杂表达式
     */
    private static class ComplexExpression extends Expression {
        @Override
        int interpreter(String text) {
//            System.out.println("ComplexExpression:" + text);
            int letNum=0;
            int rightNum=0;
            char opera = ' ';

            Pattern pattern = Pattern.compile("[\\+\\-\\*\\/]");
            Matcher matcher = pattern.matcher(text);
            if (matcher.find()) {
                int start = matcher.start();
                opera = text.charAt(start);
                if (text.indexOf("(") == 0) { // 在操作符前面有括号, 先计算括号的内容
                    int endBracketPos = findEndBracketPos(text);
                    letNum = new ComplexExpression().interpreter(text.substring(1,endBracketPos));
                    if (endBracketPos == text.length() -1 ) {
                        return letNum;
                    }
                    opera = text.charAt(endBracketPos+1);
                    rightNum = new ComplexExpression().interpreter(text.substring(endBracketPos+2));
                } else if ((opera == '*' || opera == '/') && text.charAt(start+1) != '(') { // 需要先完成左边运算
                    boolean hasNext = matcher.find(start + 1);
                    if (hasNext) {
                        int pos2 = matcher.start();
                        letNum = new ComplexExpression().interpreter(text.substring(0,pos2));
                        opera = text.charAt(pos2);
                        rightNum = new ComplexExpression().interpreter(text.substring(pos2+1));
                    } else {
                        letNum = new TerminalExpression().interpreter(text.substring(0,start));
                        rightNum = new ComplexExpression().interpreter(text.substring(start+1));
                    }

                } else {
                    letNum = new TerminalExpression().interpreter(text.substring(0,start));
                    rightNum = new ComplexExpression().interpreter(text.substring(start+1));
                }
                return compute(letNum,rightNum,opera);
            } else { // 终结表达式
                return new TerminalExpression().interpreter(text);
            }
        }

        private int findEndBracketPos(String text) {
            int startPos = 0,endPos = 0;
            do {
                endPos = text.indexOf(")",endPos+1);
                startPos = text.indexOf("(",startPos+1);
            } while (startPos < endPos && startPos > 0);
            return endPos;
        }

    }

    /**
     * 是一个数值 或者 是 (数值) 形式,要把text 转换为数值
     */
    private static class TerminalExpression extends Expression {
        @Override
        int interpreter(String text) {
//            System.out.println("TerminalExpression:" + text);
            if (text.indexOf("(") == 0) {
                text = text.substring(1,text.length() - 1);
            }
            return Integer.parseInt(text);
        }
    }

}

2.2 优缺点

优点:

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

缺点:

  1. 执行效率较低,使用了大量的循环和递归调用,调试过程比较麻烦。
  2. 复杂文法难以维护。如果一种语言包含太多文法规则,类的数量将会急剧增加,导致系统难以管理和维护。

相关推荐

  1. 设计模式-设计原则

    2024-06-06 11:34:10       15 阅读
  2. 设计模式-备忘录模式

    2024-06-06 11:34:10       37 阅读
  3. 设计模式——备忘录模式

    2024-06-06 11:34:10       38 阅读

最近更新

  1. 什么是DNS欺骗

    2024-06-06 11:34:10       0 阅读
  2. leetcode hot 100 刷题记录

    2024-06-06 11:34:10       0 阅读
  3. 全面解析C#:现代编程语言

    2024-06-06 11:34:10       0 阅读
  4. 【深入探索】揭秘SQL Server的多重身份验证模式

    2024-06-06 11:34:10       1 阅读
  5. 短链接day3

    2024-06-06 11:34:10       1 阅读
  6. [C++基础]C++ 10个常用案例

    2024-06-06 11:34:10       1 阅读
  7. android paddingStart paddingLeft 使用区别

    2024-06-06 11:34:10       1 阅读
  8. 【ARMv8/v9 GIC 系列 5.7 -- 中断路由与系统寄存器】

    2024-06-06 11:34:10       1 阅读
  9. python在人工智能领域中的应用

    2024-06-06 11:34:10       1 阅读
  10. 互联汽车的RF挑战和解决方案

    2024-06-06 11:34:10       1 阅读

热门阅读

  1. C#-for循环语句

    2024-06-06 11:34:10       9 阅读
  2. 2024速通python之python面向对象

    2024-06-06 11:34:10       8 阅读
  3. zigbee浅谈

    2024-06-06 11:34:10       6 阅读
  4. Leetcode373.查找和最小的 K 对数字

    2024-06-06 11:34:10       6 阅读
  5. oracle的bitmap索引是什么

    2024-06-06 11:34:10       10 阅读
  6. Oracle作业调度器Job Scheduler

    2024-06-06 11:34:10       9 阅读
  7. 解决VIvado编程中遇到的bug 4

    2024-06-06 11:34:10       11 阅读
  8. Linux 下安装docker

    2024-06-06 11:34:10       7 阅读
  9. 力扣904.水果成篮

    2024-06-06 11:34:10       8 阅读