意图:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可以将该对象回复到原先保存的状态。
动机:
有时有必要记录一个对象的内部状态。为了允许用户取消不确定的操作或从错误中恢复过来,需要实现检查点和取消机制,而要实现这些机制,必须实现将状态信息保存在某处,这样能将对象恢复到它们先前的状态。但是对象通常封装了其部分或所有的状态信息,使得其状态不能被其他对象访问,也就不可能在该对象之外保存其状态。而暴露其内部状态又将违反封装的原则,有可能有损应用的可靠性和可扩展性。
一个备忘录是一个对象,它存储另一个对象在某个瞬间的内部状态,而后者称为成为备忘录(memento)的原发器(originator)。当需要设置原发器的检查点时,取消操作机制会想原发器请求一个备忘录。原发器用描述当前状态的信息初始化备忘录。只有原发器可以向备忘录中存取信息,备忘录对其他对象“不可见”
适用性:
在以下情况使用备忘录模式:
1 必须保存一个对象在某个时刻的(部分)状态,这样以后需要时才能恢复到先前的状态
2 如果一个用接口来让其它对象直接得到这些状态,将会暴露对象的实现喜街并破坏对象的封装性。
结构:
参与者:
Memento:
1 备忘录存储原发器对象的内部状态。原发器根据需要决定备忘录存储原发器的哪些内部状态。
2 防止原发器以外的其他对象访问备忘录。备忘录实际上有两个接口,管理者(caretaker)只能看到备忘录的窄接口--只能将备忘录传递给其他对象。相反,原发器能够看到一个宽接口,允许它访问返回到先前状态所需的所有数据。理想的情况是只允许生成备忘录的那个原发器访问备忘录的内部状态。
Originator(原发器)
1 原发器创建一个备忘录,用以记录当前时刻它的内部状态;2 使用备忘录回复内部状态
Caretaker(负责人)
1 负责保存好备忘录 2 不能对备忘录的内容进行操作或检查。
协作:
管理器向原发器请求一个备忘录,保留一段时间后,将其送回给原发器.如下图:
//±¸Íü¼ģʽ
#include <iostream>
using namespace std;
class GameRole;
class RoleStateMemento
{
public:
RoleStateMemento()
{
}
RoleStateMemento(int v, int a, int d)
: vit(v), atk(a), def(d)
{
}
virtual ~RoleStateMemento()
{}
private:
friend class GameRole;
int vit;
int atk;
int def;
};
//ÓÎÏ·½ÇÉ«£¬Ï൱ÓÚOriginatorÀà
class GameRole
{
public:
GameRole(int v = 100, int a = 100, int d = 100) :
vit(v), atk(a), def(d)
{}
//״̬ÏÔʾ
void StateDisplay()
{
cout << "½ÇÉ«µ±Ç°×´Ì¬£º" << endl;
cout << "ÌåÁ¦£º" << vit << "¹¥»÷Á¦£º"
<< atk << "·ÀÓùÁ¦£º" << def << endl;
}
//±£´æ½Çɫ״̬
RoleStateMemento& SaveState()
{
return *(new RoleStateMemento(vit, atk, def));
}
//»Ö¸´½Çɫ״̬
void RecoveryState(RoleStateMemento& memento)
{
vit = memento.vit;
atk = memento.atk;
def = memento.def;
}
//Óëboss´óÕ½Ö®ºó״̬
void Fight()
{
vit = 0;
atk = 0;
def = 0;
}
private:
int vit; //ÉúÃüÁ¦
int atk; //¹¥»÷Á¦
int def; //·ÀÓùÁ¦
};
//½Çɫ״̬¹ÜÀíÕß
class RoleStateCaretaker
{
public:
RoleStateMemento& GetMemento()
{
return memento;
}
void SetMemento(RoleStateMemento& m)
{
memento = m;
}
private:
RoleStateMemento memento;
};
int main()
{
//´óÕ½BossÇ°
GameRole lixiaoyao;
lixiaoyao.StateDisplay();
//±£´æ½ø¶È
RoleStateCaretaker stateAdmin;
stateAdmin.SetMemento(lixiaoyao.SaveState());
//´óÕ½bossʱ£¬ËðºÄÑÏÖØ
lixiaoyao.Fight();
lixiaoyao.StateDisplay();
//»Ö¸´Ö®Ç°×´Ì¬
lixiaoyao.RecoveryState(stateAdmin.GetMemento());
lixiaoyao.StateDisplay();
system("pause");
return 0;
}
有时管理者不会将备忘录返回给原发器,因为原发器可能不需要退到先前的状态.
备忘录是被动的,只有创建备忘录的原发器会对它的状态进行赋值和检索.
效果:
1 保持封装边界:适用备忘录可以避免暴露一些只应由原发起管理却又必须存储在原发器之外的信息。把可能很复杂的Originator内部信息对其他对象屏蔽起来,从而保持了封装边界。
2 简化了原发器:Originator负责保持客户请求过的内部状态版本。把所有存储管理任务交给了Originator,让客户管理它们请求的状态会简化Originator,使客户工作结束时无需通知原发器。
3 适用备忘录可能代价很高;
4 定义窄接口和宽借口:在一些语言中很难保证只有原发器可访问备忘录的状态。
5 维护备忘录的潜在代价
实现:
1 语言支持 备忘录有两个接口:一个为原发器所使用的宽接口,一个为其他对象所使用的窄接口。理想的实现语言应可支持两级的静态保护。在C++中,可将Originator作为Memento的一个友元,并使Memento宽接口为私有的。只有窄借口应该被声明为共有的。
2 存储增量式改变:如果备忘录的创建及返回的顺序是可预测的,备忘录可以仅存储原发器内部状态的增量改变,而不是它所影响的每个对象的完整状态。