备忘录模式:(任务失败,回到检查点重来)
定义:
在不破坏封装性的前提下,捕捉一个对象的内部状态,并在对象之外保存这个状态,使其可以在以后将对象恢复到这个状态。
翻译:
一个类需要拥有一个备份类用来记录和恢复自己在某个时刻的状态,只需要复制自己,保留数据,删去功能函数,保留构造函数后,便是一个该类的数据备份类了,
需要备份类的类的增加两个函数,是对备份类的get和set函数。
类图摘自设计模式之禅:
深入:
但我们想要恢复某个状态时,肯定得恢复那个状态下的类以及周围受到其影响的类的状态,故备份类会和需要备份的类多起来,就需要备忘录管理类,降低类与备份类的耦合。
应用场景:
应用场景太多了,文档存档,游戏存档,游戏检查点存档,项目日志存档,数据库存档,任何需要“保存”的地方都是备忘录模式发挥作用的地方。
作用:
1.提供回滚操作(rollback),就是撤销或者上一步。
2.监控对象时,因监控不是系统主业务,一般是备份一个主线程的对象,然后由分析程序分析。
3.保存至数据库。
注意点:
备忘录的生命周期:
备忘录创建出来就是要在“最近”的代码中使用,要主动管理他的生命周期,建立就要使用,不使用就要立刻删除其引用,等待垃圾收集器对它的回收处理。
备忘录的性能:
不能再频繁建立备份的场景中使用备忘录模式,如for循环中。一是难以控制建立的对象数量,二是建立大对象消耗的资源多。
全状态备份:保留所有状态
将发起对象的所有属性值转化到hashmap中,就是哈希表,避免直接拷贝对象,直接拷贝对象就破坏了发起人的通用性,避免在恢复动作中对该对象的多次赋值操作而产生失误。
多备份的备忘录:多次备份全状态备份
给每次备份增加一个时间点并一一对应,故只需要把原容器再通过增加时间点包装一次,做成一个map就可。
注意:备份一旦产生就会装入内存,没有任何销毁的意向,这是相当危险的,因此设计系统时,要严格限定备忘录的创建,建议增加map的上限,避免系统出现内存溢出。
代码示例:这是一个简单的备忘录模式示例,一个数据类作为备份保存一个功能类的状态,备份管理类中也只有这一中备份,实际开发中的情况并非如此,留在文尾。
//数据备份类
class Memento
{
public:
Memento(int iData)
{
mData = iData;
}
int GetData()
{
return mData;
}
protected:
int mData;
};
//备份管理类
class CareTakerOfOriginator
{
public:
void SetMemento(Memento * memento)
{
mMemento = memento;
}
Memento* GetMemento()
{
if (mMemento != nullptr)
{
return mMemento;
}
else
{
return nullptr;
}
}
protected:
Memento* mMemento;
};
//备份发起和调用类
class Originator
{
public:
Originator()
{
mData = 0;
}
void StatusShow()
{
cout << "我现在的数据状态是,数据Data值为" << mData << endl;
}
Memento* SetMemento()
{
cout << "状态已存盘" << endl;
return (new Memento(mData));
}
void GetMemento(Memento* memento)
{
cout << "状态已读取" << endl;
mData = memento->GetData();
}
void action()
{
cout << "数据状态发生改变" << endl;
++mData;
}
protected:
int mData;
};
void func()
{
Originator* ZhangSan = new Originator();
CareTakerOfOriginator* ZSCareTaker = new CareTakerOfOriginator();
ZhangSan->StatusShow();
ZSCareTaker->SetMemento(ZhangSan->SetMemento());
ZhangSan->action();
ZhangSan->StatusShow();
ZhangSan->action();
ZhangSan->StatusShow();
ZhangSan->action();
ZhangSan->StatusShow();
ZhangSan->action();
ZhangSan->StatusShow();
ZhangSan->action();
ZhangSan->StatusShow();
ZhangSan->GetMemento(ZSCareTaker->GetMemento());
ZhangSan->StatusShow();
}
int main()
{
func();
return 0;
}
我们成功的使用备份复原了保存时的数据状态,但这只是一个简单而基础的演示,实际开发中的功能类数量,备份类数量,管理类的管理方法远远不会如此简单,基于安全性的考虑也不会将代码设计的如此简陋,实际中的数据备份是相当庞大的,占用极多资源的操作。