22 C++设计模式之备忘录(Memento)模式
文章目录
- 备忘录(Memento)模式定义
- 备忘录(Memento)模式优缺点
- 优点
- 缺点
- 备忘录(Memento)模式构成与实现
- 构成
- 实例
备忘录(Memento)模式定义
备忘录(Memento): 在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可以将该对象恢复到原先保存的状态
备忘录(Memento)模式优缺点
优点
- 你可以在不破坏对象封装情况的前提下创建对象状态快照。
- 你可以通过让负责人维护原发器状态历史记录来简化原发器代码
缺点
- 如果客户端过于频繁地创建备忘录,程序将消耗大量内存。
- 负责人必须完整跟踪原发器的生命周期,这样才能销毁弃用的备忘录。
- 绝大部分动态编程语言(例如 PHP、 Python 和 JavaScript)不能确保备忘录中的状态不被修改。
备忘录(Memento)模式构成与实现
构成
- 原发器(Originator)类可以生成自身状态的快照,也可以在需要时通过快照恢复自身状态。
- 备忘录 (Memento) 是原发器状态快照的值对象 (valueobject)。通常做法是将备忘录设为不可变的,并通过构造函数一次性传递数据。
- 负责人(Caretaker)仅知道“何时”和“为何”捕捉原发器的状态,以及何时恢复状态。负责人通过保存备忘录栈来记录原发器的历史状态。当原发器需要回溯历史状态时,负责人将从栈中获取最顶部的备忘录,并将其传递给原发器的恢复(restoration)方法。
- 在该实现方法中,备忘录类将被嵌套在原发器中。这样原发器就可访问备忘录的成员变量和方法,即使这些方法被声明为私有。另一方面,负责人对于备忘录的成员变量和方法的访问权限非常有限:它们只能在栈中保存备忘录,而不能修改其状态。
实例
Memento.h:
#ifndef MEMENTO_H_
#define MEMENTO_H_
#include <string>
// 备忘录类保存编辑器的过往状态
class Snapshot {
public:
Snapshot(std::string text, int x, int y, double width)
: text_(text), cur_x_(x), cur_y_(y), selection_width_(width) {}
std::string get_text() {
return text_;
}
int get_cur_x() {
return cur_x_;
}
int get_cur_y() {
return cur_y_;
}
double get_selection_width() {
return selection_width_;
}
private:
const std::string text_;
const int cur_x_;
const int cur_y_;
const double selection_width_;
};
#endif // MEMENTO_H_
Originator.h:
#ifndef ORIGINATOR_H_
#define ORIGINATOR_H_
#include <cstdio>
#include <string>
#include <memory>
#include "Memento.h"
// 原发器中包含了一些可能会随时间变化的重要数据
// 它还定义了在备忘录中保存自身状态的方法, 以及从备忘录中恢复状态的方法
class Editor {
public:
void setText(std::string text) {
text_ = text;
}
void setCursor(int x, int y) {
cur_x_ = x;
cur_y_ = y;
}
void setSelectionWidth(double width) {
selection_width_ = width;
}
// 在备忘录中保存当前的状态
std::shared_ptr<Snapshot> createSnapshot() {
// 备忘录是不可变的对象, 因此原发器会将自身状态作为参数传递给备忘录的构造函数
auto res = std::make_shared<Snapshot>(text_, cur_x_, cur_y_, selection_width_);
printf("创建编辑器快照成功, text:%s x:%d y:%d width:%.2f\n", text_.c_str(), cur_x_, cur_y_, selection_width_);
return res;
}
void resotre(std::shared_ptr<Snapshot> sptr_snapshot) {
text_ = sptr_snapshot->get_text();
cur_x_ = sptr_snapshot->get_cur_x();
cur_y_ = sptr_snapshot->get_cur_y();
selection_width_ = sptr_snapshot->get_selection_width();
printf("恢复编辑器状态成功, text:%s x:%d y:%d width:%.2f\n", text_.c_str(), cur_x_, cur_y_, selection_width_);
}
private:
// 文本
std::string text_;
// 光标位置
int cur_x_;
int cur_y_;
// 当前滚动条位置
double selection_width_;
};
#endif // ORIGINATOR_H_
Caretaker.h:
#ifndef CARETAKER_H_
#define CARETAKER_H_
#include <memory>
#include "Memento.h"
#include "Originator.h"
class Command {
public:
explicit Command(Editor* e) : editor_(e) {}
void makeBackup() {
backup_ = editor_->createSnapshot();
}
void undo() {
if (backup_) {
editor_->resotre(backup_);
}
}
private:
Editor *editor_;
std::shared_ptr<Snapshot> backup_;
};
#endif // CARETAKER_H_
main.cpp
#include "Caretaker.h"
int main() {
system("chcp 65001");
// 创建原发器和负责人
Editor editor;
Command command(&editor);
// 定义初始状态
editor.setText("TOMOCAT");
editor.setCursor(21, 34);
editor.setSelectionWidth(3.4);
// 保存状态
command.makeBackup();
// 更改编辑器状态
editor.setText("KKKK");
editor.setCursor(111, 222);
editor.setSelectionWidth(111.222);
// 撤销
command.undo();
system("pause");
return 0;
}
输出:
Active code page: 65001
创建编辑器快照成功, text:TOMOCAT x:21 y:34 width:3.40
恢复编辑器状态成功, text:TOMOCAT x:21 y:34 width:3.40
Press any key to continue . . .