• 设计模式之备忘录模式 - 简书


    备忘录模式是一种行为设计模式, 允许在不暴露对象实现细节的情况下保存和恢复对象之前的状态。

    解决方案

    我们刚才遇到的所有问题都是封装 “破损” 造成的。 一些对象试图超出其职责范围的工作。 由于在执行某些行为时需要获取数据, 所以它们侵入了其他对象的私有空间, 而不是让这些对象来完成实际的工作。

    备忘录模式将创建状态快照 (Snapshot) 的工作委派给实际状态的拥有者_原发器_ (Originator) 对象。 这样其他对象就不再需要从 “外部” 复制编辑器状态了, 编辑器类拥有其状态的完全访问权, 因此可以自行生成快照。

    模式建议将对象状态的副本存储在一个名为_备忘录_ (Memento) 的特殊对象中。 除了创建备忘录的对象外, 任何对象都不能访问备忘录的内容。 其他对象必须使用受限接口与备忘录进行交互, 它们可以获取快照的元数据 (创建时间和操作名称等), 但不能获取快照中原始对象的状态。

    这种限制策略允许你将备忘录保存在通常被称为_负责人_ (Caretakers) 的对象中。 由于负责人仅通过受限接口与备忘录互动, 故其无法修改存储在备忘录内部的状态。 同时, 原发器拥有对备忘录所有成员的访问权限, 从而能随时恢复其以前的状态。

    在文字编辑器的示例中, 我们可以创建一个独立的历史 (History) 类作为负责人。 编辑器每次执行操作前, 存储在负责人中的备忘录栈都会生长。 你甚至可以在应用的 UI 中渲染该栈, 为用户显示之前的操作历史。

    当用户触发撤销操作时, 历史类将从栈中取回最近的备忘录, 并将其传递给编辑器以请求进行回滚。 由于编辑器拥有对备忘录的完全访问权限, 因此它可以使用从备忘录中获取的数值来替换自身的状态。

    备忘录模式结构

    基于嵌套类的实现,该模式的经典实现方式依赖于许多流行编程语言 (例如 C++、 C# 和 Java) 所支持的嵌套类。

    原发器 (Originator) 类可以生成自身状态的快照, 也可以在需要时通过快照恢复自身状态。

    备忘录 (Memento) 是原发器状态快照的值对象 (value object)。 通常做法是将备忘录设为不可变的, 并通过构造函数一次性传递数据。

    负责人 (Caretaker) 仅知道 “何时” 和 “为何” 捕捉原发器的状态, 以及何时恢复状态。

    负责人通过保存备忘录栈来记录原发器的历史状态。 当原发器需要回溯历史状态时, 负责人将从栈中获取最顶部的备忘录, 并将其传递给原发器的恢复 (restoration) 方法。

    在该实现方法中, 备忘录类将被嵌套在原发器中。 这样原发器就可访问备忘录的成员变量和方法, 即使这些方法被声明为私有。 另一方面, 负责人对于备忘录的成员变量和方法的访问权限非常有限: 它们只能在栈中保存备忘录, 而不能修改其状态。

    JAVA 示例代码

    import java.util.*;
    
    public class MementoPattern {
    
        public static void main(String\[\] args) {
    
            Caretaker caretaker = new Caretaker();
    
            Originator originator = new Originator();
    
            originator.setState("1024");
    
            Memento backup1 = originator.createMemento();
    
            caretaker.addMemento(backup1);
    
            originator.setState("2048");
    
            Memento backup2 = originator.createMemento();
    
            caretaker.addMemento(backup2);
    
            originator.setState("4096");
    
            Memento backup3 = originator.createMemento();
    
            caretaker.addMemento(backup3);
    
            System.out.println(originator.getState());
    
            caretaker.showMemento();
    
            Memento memento1 = caretaker.getMemento(2);
    
            originator.setMemento(memento1);
    
            System.out.println("根据第2次备份还原之后的状态为:" + originator.getState());
    
        }
    
    }
    
    class Originator { // 原发器
    
        private String state;
    
        public void setState(String state) {
    
            this.state = state;
    
        }
    
        public String getState() {
    
            return state;
    
        }
    
        public Memento createMemento() {
    
            return new Memento(state);
    
        }
    
        public void setMemento(Memento memento) {
    
            state = memento.getState();
    
        }
    
    }
    
    class Memento { // 备忘录
    
        private String state;
    
        public Memento(String state) {
    
            this.state = state;
    
        }
    
        public String getState() {
    
            return state;
    
        }
    
    }
    
    class Caretaker { // 管理者
    
        private List<Memento> mementoList = new ArrayList<>();
    
        public void addMemento(Memento memento) {
    
            mementoList.add(memento);
    
        }
    
        public Memento getMemento(int index) {
    
            // 判断参数是否合法
    
            if (index >= 1 && index <= mementoList.size()) {
    
                return mementoList.get(index - 1);
    
            }
    
            return null;
    
        }
    
        public void showMemento() {
    
            int cnt = 1;
    
            // for (遍历对象类型 对象名 : 遍历对象)
    
            for (Memento memento : mementoList) {
    
                System.out.println("第" + cnt + "次备份,状态为:" + memento.getState());
    
                cnt ++ ;
    
            }
    
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
  • 相关阅读:
    CAS:111790-37-5_Biotin-NH2_生物素-氨基 生物素化化合物
    俄罗斯卢布对美元接近60 卢布今年一直是全球表现最好的货币?
    利用ipv6把电脑做成服务器(ssl443端口)
    CentOs7 配置jar包开机自启动
    前端程序员兼职副业平台推荐
    网络代码理解
    【论文笔记】—低照度图像增强—Unsupervised—EnlightenGAN—2019-TIP
    Vue源码用到了哪些设计模式
    Vue基础入门
    RKMPP库快速上手--(二)MPP关键配置
  • 原文地址:https://blog.csdn.net/flysh05/article/details/127578913