概述

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

结构1

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

结构2

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

场景

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

联系

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

示例1

memento.cpp

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

示例2

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

示例3

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

    Powerful Number 筛学习笔记 用途 \(Powerful\ number\) 筛可以用来求出一类积性函数的前缀和,最快可以达到根号复杂度. 实现 \(Powerful\ number\) ...

  2. mysql中FIND_IN_SET函数用法

    本篇文章主要介绍mysql中FIND_IN_SET函数用法,用来精确查询字段中以逗号分隔的数据 以及其与 like 和 in 的区别 1.问题发现 之前在做工作任务时有这么一个需求:需要用接口所传的服 ...

  3. 学会使用 Mysql show processlist 排查问题

    mysql show full processlist 查看当前线程处理情况 事发现场 每次执行看到的结果应该都有变化,因为是实时的,所以我定义为:"事发现场",每次执行就相当于现 ...

  4. 呵呵,Semaphore,就这?

    这是并发线程工具类的第二篇文章,在第一篇中,我们分析过 CountDownLatch 的相关内容,你可以参考 一文搞懂 CountDownLatch 用法和源码! 那么本篇文章我们继续来和你聊聊并发工 ...

  5. JavaWeb 补充(Json)

    HTML DOM alert() 方法 定义和用法 alert() 方法用于显示带有一条指定消息和一个 OK 按钮的警告框. 参数 描述 message 要在 window 上弹出的对话框中显示的纯文 ...

  6. 告别DNS劫持,一文读懂DoH

    如果评选一个差评服务器榜单,除去育碧高居榜首外,一定也少不了 Nintendo Switch 让人头秃的联网服务.尽管任天堂已经架设了香港 CDN 服务器用于加速,但是更新安装的速度也没有什么大幅改变 ...

  7. JUC包的线程池详解

    为什么要使用线程池 创建/销毁线程需要消耗系统资源,线程池可以复用已创建的线程. 控制并发的数量.并发数量过多,可能会导致资源消耗过多,从而造成服务器崩溃.(主要原因) 可以对线程做统一管理. JUC ...

  8. 基于MATLAB的手写公式识别(2)

    基于MATLAB的手写公式识别 图像的预处理(除去噪声.得到后续定位分割所需的信息.) 预处理其本质就是去除不需要的噪声信息,得到后续定位分割所需要的图像信息.图像信息在采集的过程中由于天气环境的影响 ...

  9. [C#] NAudio 库的各种常用使用方式: 播放 录制 转码 音频可视化

    概述 在 NAudio 中, 常用类型有 WaveIn, WaveOut, WaveStream, WaveFileWriter, WaveFileReader 以及接口: IWaveProvider ...

  10. Java基础常用类深度解析(包含常见排序算法)

    目录 一.工具类 1.1.工具类的设计 1.1.1.公共静态方法 1.2.单例模式 二.包装类 2.1.基本类型的包装类 2.1.1.Integer 2.1.1.1.Integer >> ...