本文是学习刘伟技术博客和《设计模式-可复用面向对象软件的基础》笔记,博客链接:http://blog.csdn.net/lovelion/article/details/17517213
主要是对博客和书本做提炼和记录,更多是对设计模式的基础框架学习,细节将略去,侧重对每个设计模式框架的理解。
我应该理解和掌握的:
1)能够画出这个设计模式的架构框图;
2)能够根据架构框图写出对应的伪代码;
3)这个模式的应用场景,主要优缺点。
1.备忘录模式
通常我们在写程序的时候,都会用到control+z来回退到之前的状态,这个指令用于撤销修改。这种现象可以粗糙的比喻为备忘录模式。编译器存储了之前的历史状态,只要我们简单的ctrl+z就可以恢复到之前的状态。备忘录模式提供了一种状态恢复的实现机制(通过先存储当前的状态),使得我们可以返回到特定的历史状态。
(1)定义
备忘录模式:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可以将该对象恢复到原先的状态。别名又称:Token.
1) 备忘录模式结构图
一个备忘录是一个对象,它存储另一个对象在某个瞬间的内部状态,而后者成为备忘录的原发器。只有原发器可以向备忘录中存储信息,备忘录对其他的对象不可见。
2) 参与者
a) Memento(备忘录):
----备忘录存储原发器的内部状态。原发器根据需要决定备忘录存储原发器的哪些内部状态。
----防止原发器以外的其他对象访问备忘录。备忘录实际上有两个接口,管理者(Caretaker)只能看到窄接口,只能将备忘录传递给其他对象,不能访问备忘录的内部数据。原发器看到宽接口,允许它访问返回到先前状态所需的所有数据。理想情况下,只允许生成本备忘录的那个原发器访问备忘录的内部状态。
b) Originator(原发器):原发器创建一个备忘录,用以记录当前时刻他的内部状态。使用备忘录来恢复内部状态。
c) Caretaker(负责人):负责保存好备忘录。不能对备忘录内容进行检查或操作。
3) 看图写代码
#include<iostream>
using namespace std;
#define MAX 1024
//定义一个原发器的内部状态
struct State
{
char state[MAX];
};
//定义一个备忘录
class Memento
{
public:
virtual ~Memento()
{
if(_state)
{
delete _state;
_state = NULL;
}
}
private:
//设置原发器为友元类,可以访问备忘录的内部状态
friend class Originator;
Memento(State* s):_state(s){}
private:
State* _state;
};
//定义一个原发器
class Originator
{
public:
Originator():_state(NULL){}
~Originator()
{
if(_state)
{
delete _state;
_state = NULL;
}
}
//创建一个备忘录对象
Memento* createMemeto()
{
State* s = new State;
memset(s->state, 0, MAX * sizeof(char));
strcpy(s->state, _state->state);
Memento * m = new Memento(s);
return m;
}
//恢复到备忘录对象存储的状态
void restoreMemento(Memento* m)
{
if(_state)
{
delete _state;
_state = NULL;
}
_state = new State;
memset(_state->state, 0, MAX * sizeof(char));
strcpy(_state->state, m->_state->state);
}
//输出原发器的状态
void printState()
{
cout<<_state->state<<endl;
}
//设置原发器的状态
void setState(char *s)
{
if(_state)
{
delete _state;
_state = NULL;
}
_state = new State;
memset(_state->state, 0, MAX * sizeof(char));
strcpy(_state->state, s);
}
private:
State* _state;
};
//定义一个负责人对象
class Caretaker
{
public:
Caretaker():_memento(NULL){}
~Caretaker()
{
if(_memento)
{
delete _memento;
_memento = NULL;
}
}
//注入备忘录对象
void setMemento(Memento* m)
{
if(_memento)
{
delete _memento;
_memento = NULL;
}
_memento = m;
}
//返回备忘录对象
Memento* getMemento()
{
return _memento;
}
private:
Memento* _memento;
};
//客户端测试代码
#define SAFE_DELETE(p) if(p){delete p; p = NULL;}
int main(int argc, const char * argv[]) {
//创建生成器对象
Originator* pr = new Originator();
pr->setState("first");
pr->printState();
//创建负责人对象
Caretaker* ct = new Caretaker();
//创建,存储备忘录对象
ct->setMemento(pr->createMemeto());
pr->setState("second");
pr->printState();
//恢复到之前的状态
pr->restoreMemento(ct->getMemento());
pr->printState();
SAFE_DELETE(ct);
SAFE_DELETE(pr);
return 0;
}
(2)总结
1) 优点
a) 保持封装边界;使用备忘录可以避免暴露一些只应当由原发器管理却又必须存储在原发器之外的信息。
b) 简化了原发器。
c) 提供一种状态恢复机制,使得用户可以方便的回复到一个特定的历史步骤,当新的状态无效或无效,可以使用暂时存储起来的备忘录将状态复原。
d) 备忘录实现了对信息的封装,一个备忘录对象是一种原发器对象的表示,不会被其他代码所改动。备忘录保存了原发器的状态,采用列表、堆栈等集合来存储备忘录对象可以实现多次撤销操作。
2) 缺点
a) 使用备忘录可能代价过高。假如原发器生成备忘录时必须拷贝并存储大量的信息。或者客户端非常频繁地创建备忘录和恢复原发器状态,可能导致非常大的开销。
(3)适用场景
1)必须保存一个对象在某一个时刻的状态,这样以后需要时它才能恢复到先前的状态。
2)防止外界对象破坏一个对象历史状态的封装性,避免将对象历史状态的实现细节暴露给外界对象。