概述

  • 也叫快照(SnapShot)
  • 属于行为类设计模式
  • 允许在不暴露对象实现细节的情况下保存和恢复对象之前的状态
  • 软件构建过程中,某些对象的状态在转换过程中,可能由于某种需要,要求程序能回溯到对象之前处于某个点时的状态,如果使用一些公有接口来让其他对象得到对象的状态,便会暴露对象的细节实现
  • 如何实现对象状态的良好保存与恢复,但同时又不会因此而破坏对象本身的封装性
  • 在不破坏封装性的前提下,捕捉一个对象的内部状态,并在该对象之外保存这个状态,这样以后就可以将该对象恢复到原先保存的状态
  • 备忘录(Memento)存储原发器(Originator)对象的内部状态,在需要时恢复原发器状态
  • Memento模式的核心是信息隐藏,即Originator需要向外接隐藏信息,保持其封装性,但同时又需要将状态保持到外界(Memento)
  • 由于现代语言(Java,C#)运行时,都具有相当的序列化支持,因此往往用效率较高,又较容易正确实现的序列化方案来实现Memento模式

结构1

  • 原发器类:用于获取状态,生成备忘录对象(快照),并在需要时通过快照恢复状态
  • 备忘录类:用于保存状态,通常将备忘录设为不可变的,同通过构造函数一次性传递数据
  • 负责人类:仅知道“何时”及“为何”捕捉原发器的状态,以及何时恢复状态,通过备忘录栈记录原发器的历史状态,当原发器要追溯历史状态时,负责人获取栈顶备忘录,传递给原发器的恢复方法
  • 备忘录类嵌套在原发器类中,原发器就可以访问备忘录的成员变量和方法

结构2

  • 允许存在多种不同类型的原发器和备忘录,每种原发器都和其响应的备忘录类进行交互
  • 负责人被禁止修改在备忘录中的状态,恢复方法被定义在了备忘录类中
  • 每个备忘录与创建了自身的原发器连接,原发器将自己及状态传递给备忘录的构造函数

场景

  • 开发一款文字编辑器应用,让用户可以撤销施加在文本上的任何操作。可让程序在执行前记录所有的对象状态,并将其保存下来,当用户以后撤销某个操作时,程序将从历史记录中获取最近的快照,然后恢复所有对象的状态

联系

  • 同时使用命令模式和备忘录模式实现撤销,命令用于对目标对象执行各种不同的操作,备忘录用于保存命令执行前对象的状态
  • 同时使用备忘模式和迭代器模式获取当前迭代器的状态,并在需要时进行回滚
  • 对象状态比较简单时,原型模式可以作为备忘录的一个简化版本

示例1

memento.cpp

  1. 1 class Memento
  2. 2 {
  3. 3 string state;
  4. 4 //..
  5. 5 public:
  6. 6 Memento(const string & s) : state(s) {}
  7. 7 string getState() const { return state; }
  8. 8 void setState(const string & s) { state = s; }
  9. 9 };
  10. 10
  11. 11 class Originator
  12. 12 {
  13. 13 string state;
  14. 14 //....
  15. 15 public:
  16. 16 Originator() {}
  17. 17 Memento createMomento() {
  18. 18 Memento m(state);
  19. 19 return m;
  20. 20 }
  21. 21 void setMomento(const Memento & m) {
  22. 22 state = m.getState();
  23. 23 }
  24. 24 };
  25. 25
  26. 26 int main()
  27. 27 {
  28. 28 Originator orginator;
  29. 29
  30. 30 //捕获对象状态,存储到备忘录
  31. 31 Memento mem = orginator.createMomento();
  32. 32
  33. 33 //... 改变orginator状态
  34. 34
  35. 35 //从备忘录中恢复
  36. 36 orginator.setMomento(memento);
  37. 37
  38. 38 }

示例2

  1. 1 // 原发器中包含了一些可能会随时间变化的重要数据。它还定义了在备忘录中保存
  2. 2 // 自身状态的方法,以及从备忘录中恢复状态的方法。
  3. 3 class Editor is
  4. 4 private field text, curX, curY, selectionWidth
  5. 5
  6. 6 method setText(text) is
  7. 7 this.text = text
  8. 8
  9. 9 method setCursor(x, y) is
  10. 10 this.curX = curX
  11. 11 this.curY = curY
  12. 12
  13. 13 method setSelectionWidth(width) is
  14. 14 this.selectionWidth = width
  15. 15
  16. 16 // 在备忘录中保存当前的状态。
  17. 17 method createSnapshot():Snapshot is
  18. 18 // 备忘录是不可变的对象;因此原发器会将自身状态作为参数传递给备忘
  19. 19 // 录的构造函数。
  20. 20 return new Snapshot(this, text, curX, curY, selectionWidth)
  21. 21
  22. 22 // 备忘录类保存有编辑器的过往状态。
  23. 23 class Snapshot is
  24. 24 private field editor: Editor
  25. 25 private field text, curX, curY, selectionWidth
  26. 26
  27. 27 constructor Snapshot(editor, text, curX, curY, selectionWidth) is
  28. 28 this.editor = editor
  29. 29 this.text = text
  30. 30 this.curX = curX
  31. 31 this.curY = curY
  32. 32 this.selectionWidth = selectionWidth
  33. 33
  34. 34 // 在某一时刻,编辑器之前的状态可以使用备忘录对象来恢复。
  35. 35 method restore() is
  36. 36 editor.setText(text)
  37. 37 editor.setCursor(curX, curY)
  38. 38 editor.setSelectionWidth(selectionWidth)
  39. 39
  40. 40 // 命令对象可作为负责人。在这种情况下,命令会在修改原发器状态之前获取一个
  41. 41 // 备忘录。当需要撤销时,它会从备忘录中恢复原发器的状态。
  42. 42 class Command is
  43. 43 private field backup: Snapshot
  44. 44
  45. 45 method makeBackup() is
  46. 46 backup = editor.createSnapshot()
  47. 47
  48. 48 method undo() is
  49. 49 if (backup != null)
  50. 50 backup.restore()
  51. 51 // ...

示例3

  1. 1 #include <string>
  2. 2 #include <vector>
  3. 3 #include <iostream>
  4. 4 #include <ctime>
  5. 5 using namespace std;
  6. 6
  7. 7 class Memento{
  8. 8 public:
  9. 9 virtual string GetName() const = 0;
  10. 10 virtual string date() const = 0;
  11. 11 virtual string state() const = 0;
  12. 12 };
  13. 13
  14. 14 class ConcreteMemento:public Memento{
  15. 15 private:
  16. 16 string state_;
  17. 17 string date_;
  18. 18 public:
  19. 19 ConcreteMemento(string state):state_(state){
  20. 20 this->state_ = state;
  21. 21 time_t now = time(0);
  22. 22 this->date_ = ctime(&now);
  23. 23 }
  24. 24 string state() const override{
  25. 25 return this->state_;
  26. 26 }
  27. 27 string GetName() const override{
  28. 28 return this->date_ + "/(" + this->state_.substr(0,9)+"...)";
  29. 29 }
  30. 30 string date() const override{
  31. 31 return this->date_;
  32. 32 }
  33. 33 };
  34. 34
  35. 35 class Originator{
  36. 36 private:
  37. 37 string state_;
  38. 38 string GenereateRandomString(int length = 10){
  39. 39 const char alphanum[] =
  40. 40 "0123456789"
  41. 41 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  42. 42 "abcdefghijklmnopqrstuvwxyz";
  43. 43 int stringLength = sizeof(alphanum) - 1;
  44. 44
  45. 45 string random_string;
  46. 46 for(int i = 0; i < length; i ++){
  47. 47 random_string += alphanum[rand()%stringLength];
  48. 48 }
  49. 49 return random_string;
  50. 50 }
  51. 51 public:
  52. 52 Originator(string state):state_(state){
  53. 53 cout << "Originator: My initial state is: " << this->state_ << "\n";
  54. 54 }
  55. 55
  56. 56 void DoSomething(){
  57. 57 cout << "Originator: I'm doing something important.\n";
  58. 58 this->state_ = this->GenereateRandomString(30);
  59. 59 cout << "Originator: and my state has changed to: " << this->state_ << "\n";
  60. 60 }
  61. 61
  62. 62 Memento *Save(){
  63. 63 return new ConcreteMemento(this->state_);
  64. 64 }
  65. 65
  66. 66 void Restore(Memento *memento){
  67. 67 this->state_ = memento->state();
  68. 68 cout << "Originator: My state has changed to: " << this->state_ << "\n";
  69. 69 }
  70. 70 };
  71. 71
  72. 72 class Caretaker{
  73. 73 private:
  74. 74 vector<Memento *> mementos_;
  75. 75 Originator *originator_;
  76. 76 public:
  77. 77 Caretaker(Originator *originator):originator_(originator){
  78. 78 this->originator_ = originator;
  79. 79 }
  80. 80
  81. 81 void Backup(){
  82. 82 cout << "\nCaretaker: Saving Originator's state...\n";
  83. 83 this->mementos_.push_back(this->originator_->Save());
  84. 84 }
  85. 85 void Undo(){
  86. 86 if(!this->mementos_.size()){
  87. 87 return;
  88. 88 }
  89. 89 Memento *memento = this->mementos_.back();
  90. 90 this->mementos_.pop_back();
  91. 91 cout << "Caretaker: Restoring state to: " << memento->GetName() << "\n";
  92. 92 try{
  93. 93 this->originator_->Restore(memento);
  94. 94 }catch(...){
  95. 95 this->Undo();
  96. 96 }
  97. 97 }
  98. 98 void ShowHistory() const{
  99. 99 cout << "Caretaker: Here's the list of mementos:\n";
  100. 100 for(Memento *memento : this->mementos_){
  101. 101 cout << memento->GetName() << "\n";
  102. 102 }
  103. 103 }
  104. 104 };
  105. 105
  106. 106 void ClientCode(){
  107. 107 Originator *originator = new Originator("Super-duper-super-puper-super.");
  108. 108 Caretaker *caretaker = new Caretaker(originator);
  109. 109 caretaker->Backup();
  110. 110 originator->DoSomething();
  111. 111 caretaker->Backup();
  112. 112 originator->DoSomething();
  113. 113 caretaker->Backup();
  114. 114 originator->DoSomething();
  115. 115 cout << "\n";
  116. 116 caretaker->ShowHistory();
  117. 117 cout << "\nClient: Now, let's rollback!\n\n";
  118. 118 caretaker->Undo();
  119. 119 cout << "\nClient: Once more!\n\n";
  120. 120 caretaker->Undo();
  121. 121 delete originator;
  122. 122 delete caretaker;
  123. 123 }
  124. 124
  125. 125 int main(){
  126. 126 srand(static_cast<unsigned int>(time(NULL)));
  127. 127 ClientCode();
  128. 128 return 0;
  129. 129 }

参考

https://refactoringguru.cn/design-patterns/memento

[设计模式] 设计模式课程(十六)-- 备忘录模式(Memento)的更多相关文章

  1. C#设计模式之二十二备忘录模式(Memento Pattern)【行为型】

    一.引言 今天我们开始讲“行为型”设计模式的第十个模式,该模式是[备忘录模式],英文名称是:Memento Pattern.按老规矩,先从名称上来看看这个模式,个人的最初理解就是对某个对象的状态进行保 ...

  2. C#设计模式之二十二备忘录模式(Memeto Pattern)【行为型】

    一.引言   今天我们开始讲"行为型"设计模式的第十个模式,该模式是[备忘录模式],英文名称是:Memento Pattern.按老规矩,先从名称上来看看这个模式,个人的最初理解就 ...

  3. javascript设计模式学习之十六——状态模式

    一.状态模式的定义 状态模式的关键是区分事务内部和外部的状态,事务内部状态改变往往会带来事务的行为改变. 状态模式中有意思的一点是,一般我们谈到封装,都是优先封装对象的行为,而非对象的状态.但在状态模 ...

  4. 备忘录模式 Memento 快照模式 标记Token模式 行为型 设计模式(二十二)

    备忘录模式 Memento   沿着脚印,走过你来时的路,回到原点.     苦海翻起爱恨   在世间难逃避命运   相亲竟不可接近   或我应该相信是缘份   一首<一生所爱>触动了多少 ...

  5. Java 设计模式系列(十八)备忘录模式(Memento)

    Java 设计模式系列(十八)备忘录模式(Memento) 备忘录模式又叫做快照模式(Snapshot Pattern)或Token模式,是对象的行为模式.备忘录对象是一个用来存储另外一个对象内部状态 ...

  6. 二十四种设计模式:备忘录模式(Memento Pattern)

    备忘录模式(Memento Pattern) 介绍在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态.这样以后就可将该对象恢复到保存的状态. 示例有一个Message实体类,某 ...

  7. 乐在其中设计模式(C#) - 备忘录模式(Memento Pattern)

    原文:乐在其中设计模式(C#) - 备忘录模式(Memento Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 备忘录模式(Memento Pattern) 作者:webabc ...

  8. 模板方法模式 Template method 行为型 设计模式(二十六)

    模板方法模式 Template method 上图为网上百度的一份简历模板截图   相信大家都有求职的经历,那么必然需要简历,写简历的时候,很可能你会网上检索一份简历模板,使用此模板的格式,然后替换为 ...

  9. 设计模式之备忘录模式(Memento)

    备忘录模式(Memento) 在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态.这样以后就可将该对象恢复到原先保存的状态. Originator(发起人):负责创建一个备忘录 ...

  10. Java进阶篇设计模式之十二 ---- 备忘录模式和状态模式

    前言 在上一篇中我们学习了行为型模式的策略模式(Strategy Pattern)和模板模式(Template Pattern).本篇则来学习下行为型模式的两个模式,备忘录模式(Memento Pat ...

随机推荐

  1. cordova app打包apk签名

    首先执行:ionic cordova build android --prod --release,执行完会在以下目录生成apk文件( --prod 用以压缩) 然后使用keytool生成keysto ...

  2. 问题笔记 - element表格 操作状态值

    1.必须从传到表里的数据源中取值(scope.row.star)

  3. Java利用线程工厂监控线程池

    目录 ThreadFactory 监控线程池 扩展线程池 扩展线程池示例 优化线程池大小 线程池死锁 线程池异常信息捕获 ThreadFactory 线程池中的线程从哪里来呢?就是ThreadFoct ...

  4. [SIGIR2020] Sequential Recommendation with Self-Attentive Multi-Adversarial Network

    这篇论文主要提出了一个网络,成为Multi-Factor Generative Adversarial Network,直接翻译过来的话就是多因子生成对抗网络.主要是期望能够探究影响推荐的其他因子(因 ...

  5. Nginx常用部分命令

    Nginx一些命令 Windows 查看帮助信息 nginx -h 查看 nginx 版本 (小写字母 v) nginx -v 除版本信息外还显示配置参数信息 (大写字母 V) nginx -V 启动 ...

  6. [Fundamental of Power Electronics]-PART II-9. 控制器设计-9.1 引言

    9.1 引言 在所有的开关变换器中,输出电压\(v(t)\)都是输入电压\(v_{g}(t)\),占空比\(d(t)\),负载电流\(i_{load}(t)\)和电路元件值的函数.在DC-DC变换器应 ...

  7. 2020 OO 第一单元总结 表达式求导

    title: BUAA-OO 第一单元总结 date: 2020-03-19 20:53:41 tags: OO categories: 学习 OO第一单元通过三次递进式的作业让我们实现表达式求导,在 ...

  8. 2-fabric网络搭建流程

    目录 一.示例网络 下面开始一步步的搭建和叙述上述过程 二.创建网络 三.添加网络管理员 四.定义联盟 五.为联盟创建通道 六.节点和账本 七.应用程序和智能合约链码 八.完成网络 简化视觉词汇表 九 ...

  9. 通过Dapr实现一个简单的基于.net的微服务电商系统(二)——通讯框架讲解

    首先感谢张队@geffzhang公众号转发了上一篇文章,希望广大.neter多多推广dapr,让云原生更快更好的在.net这片土地上落地生根. 目录:一.通过Dapr实现一个简单的基于.net的微服务 ...

  10. Linux 递归修改后缀名

    1 修改命令 需要用到: find awk xargs 递归修改命令如下: find . -name '*.XXX' | awk -F "." '{print $2}' | xar ...