文章目录
备忘录模式
备忘录模式提供了一种状态恢复的实现机制 ,使得用户可以方便地回到一个特定的历史步骤,当新的状态无效或者存在问题时,可以使用暂时存储起来的备忘录将状态复原,当前很多软件都提供了撤销( Undo)操作,其中就使用了备忘录模式。备忘录模式又称为标记(Token)模式。
备忘录模式是一种软件设计模式,它提供了将对象恢复到其先前状态(通过回滚撤消)的能力,在不破坏封装的前提下捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对 象恢复到原先的状态。
结构

在备忘录模式结构图中包含如下几个角色:
- Originator(原发器) :它是一个普通类,可以创建一个备忘录,并存储它的当前内部状态,也可以使用备忘录来恢复其内部状态,一般将需要保存内部状态的类设计为原发器。
- Memento(备忘录) :*存储原发器的内部状态,根据原发器来决定保存哪些内部状态。*备忘录的设计一般可以参考原发器的设计,根据实际需要确定备忘录类中的属性。需要注意的是,除了原发器本身与负责人类之外,备忘录对象不能直接供其他类使用,原发器的设计在不同的编程语言中实现机制会有所不同。
- Caretaker(负责人) :负责人又称为管理者,它负责保存备忘录,但是不能对备忘录的内容进行操作或检查。在负责人类中可以存储一个或多个备忘录对象,它只负责存储对象,而不能修改对象,也无须知道对象的实现细节。
注意
在设计备忘录类时需要考虑其封装性,除了Originator类,不允许其他类来调用备忘录类Memento的构造函数与相关方法 ,如果不考虑封装性,允许其他类调用setState()等方法,将导致在备忘录中保存的历史状态发生改变,通过撤销操作所恢复的状态就不再是真实的历史状态,备忘录模式也就失去了本身的意义。
在使用Java语言实现备忘录模式时,一般通过将Memento类与Originator类定义在同一个包中来实现封装,在Java语言中可使用默认访问标识符来定义Memento类,即保证其包内可见。
实现

cpp
// 历史备忘录类
class History
{
public:
History(string msg) : m_msg(msg) {}
string getHistory()
{
return m_msg;
}
private:
string m_msg;
};
cpp
// 鬼子
class JiaoPenJi
{
public:
//代表鬼子在我国犯下了某一个罪行,参数对应的就是相关的描述
void setState(string msg)
{
m_msg = msg;
}
//得到鬼子犯下的罪行的相关信息
string getState()
{
return m_msg;
}
void beiDaddyDa()
{
cout << "脚盆鸡被兔子狠狠地揍了又揍, 终于承认了它们之前不承认的这些罪行: " << endl;
}
//将鬼子的罪行封装成一个历史对象,并通过返回值传出
History* saveHistory()
{
return new History(m_msg);
}
//从一个历史对象中,读出相关的历史信息
void getStateFromHistory(History* history)
{
m_msg = history->getHistory();
}
private:
string m_msg;
};
cpp
// 记录者
class Recorder
{
public:
//添加历史信息,并保存
void addHistory(int index, History* history)
{
m_history.insert(make_pair(index, history));
}
//从备份信息中得到想要的历史信息
History* getHistory(int index)
{
if (m_history.find(index) != m_history.end())
{
return m_history[index];
}
return nullptr;
}
private:
map<int, History*> m_history;
};
cpp
int main()
{
vector<string> msg{
"不承认偷了中国的中医!!!",
"不承认发动了侵华战争!!!",
"不承认南京大屠杀!!!!",
"不承认琉球群岛和钓鱼岛是中国的领土!!!",
"不承认731部队的细菌和人体试验!!!",
"不承认对我国妇女做出畜生行为!!!",
"不承认从中国掠夺的数以亿计的资源和数以万计的文物!!!",
"我干的龌龊事儿罄竹难书, 就是不承认......"
};
JiaoPenJi* jiaopen = new JiaoPenJi;
Recorder* recorder = new Recorder;
// 把小日本的罪行记录下来
for (int i = 0; i < msg.size(); ++i)
{
jiaopen->setState(msg.at(i));
recorder->addHistory(i, jiaopen->saveHistory());
}
jiaopen->beiDaddyDa();
for (int i = 0; i < msg.size(); ++i)
{
jiaopen->getStateFromHistory(recorder->getHistory(i));
cout << " -- " << jiaopen->getState() << endl;
}
return 0;
}
特点
⚠️备忘录模式在很多软件的使用过程中普遍存在,但是在应用软件开发中,它的使用频率并不太高,因为现在很多基于窗体和浏览器的应用软件并没有提供撤销操作。
主要优点
- 它提供了一种状态恢复的实现机制,使得用户可以方便地回到一个特定的历史步骤,当新的状态无效或者存在问题时,可以使用暂时存储起来的备忘录将状态复原。
- 备忘录实现了对信息的封装,一个备忘录对象是一种原发器对象状态的表示,不会被其他代码所改动。备忘录保存了原发器的状态,采用列表、堆栈等集合来存储备忘录对象可以实现多次撤销操作。
- 资源消耗过大,如果需要保存的原发器类的成员变量太多,就不可避免需要占用大量的存储空间,每保存一次对象的状态都需要消耗一定的系统资源。
适用环境
- 保存一个对象在某一个时刻的全部状态或部分状态,这样以后需要时它能够恢复到先前的状态,实现撤销操作。
- 防止外界对象破坏一个对象历史状态的封装性,避免将对象历史状态的实现细节暴露给外界对象。