分享
 
 
 

大卫的Design Patterns学习笔记18:Memento

王朝other·作者佚名  2006-01-10
窄屏简体版  字體: |||超大  

一、概述

Memento(备忘录)模式在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。

二、结构

Memento模式的类图结构如下图所示:

图1、Memento模式类图示意

Memento模式所涉及的角色有三个,备忘录角色、发起人角色和负责人角色。

其中:

Memento(备忘录):负责存储原发器对象的内部状态,并可防止原发器以外的其他对象访问备忘录。

Originator(原发器):负责创建一个备忘录,用以记录当前时刻它的内部状态,并可使用备忘录恢复内部状态。原发器可根据需要决定备忘录存储原发器的哪些内部状态,因此,当需要保存全部信息时,可以考虑用clone的方式来实现Memento的状态保存方法,但是如果是这样的话,我们有时候可能会考虑不使用Memento,而是直接保存Originator本身,但这样使得我们相当于对上层应用开放了Originator的全部(public)接口,这对于保存备份有时候是不必要的。

Caretaker(负责人):负责保存好备忘录,并且往往不能对备忘录的内容进行操作或检查。

备忘录实际上有两个接口,Caretaker只能看到备忘录的窄接口,即它只能将备忘录传递给其他对象。而原发器能够看到一个宽接口,允许它访问返回到先前状态所需的所有数据。理想的情况是只允许生成本备忘录的那个原发器访问本备忘录的内部状态。

举一个实际的例子(非Software Application),以Windows系统备份为例:备份(Memento)是备忘录角色、Windows系统(WindowsSystem)类是发起人角色、用户(User)类是负责人角色。用户不关心备份的内部细节,而且也无法直接对备份的内容进行直接修改,但Windows系统则可以(在用户指定的情况下)决定备份什么内容,以及如何还原备份。

三、应用

以下情况下可考虑使用Memento模式:

1、必须保存一个对象在某一个时刻的(部分)状态,这样以后需要时它才能恢复到先前的状态;

2、如果一个用接口来让其它对象直接得到这些状态,将会暴露对象的实现细节并破坏对象的封装性。

四、优缺点

Memento模式有以下这些优缺点:

1、保持封装边界 使用备忘录可以避免暴露一些只应由原发器管理却又必须存储在原发器之外的信息。该模式把可能很复杂的Originator内部信息对其他对象屏蔽起来,从而保持了封装边界。

2、它简化了原发器 在其他的保持封装性的设计中,Originator负责保持客户请求过的内部状态版本。这就把所有存储管理的重任交给了Originator。让客户管理它们请求的状态将会简化Originator,并且使得客户工作结束时无需通知原发器。

3、使用备忘录可能代价很高 如果原发器在生成备忘录时必须拷贝并存储大量的信息,或者客户非常频繁地创建备忘录和恢复原发器状态,可能会导致非常大的开销。除非封装和恢复Originator状态的开销不大,否则该模式可能并不合适。

4、维护备忘录的潜在代价 管理器负责删除它所维护的备忘录。然而,管理器不知道备忘录中有多少个状态。因此当存储备忘录时,一个本来很小的管理器,可能会产生大量的存储开销。

五、举例

正如结构部分所说,我们有时候可能会考虑直接保存Originator本身,而不是另外抽象出一个Memento类,就好像在Command模式笔记中举的AdjustUndoableEdit的例子一样,保存的是实际的ModelObject的clone,而不是另外写一个Memento类来。

因此,Memento模式往往被我们忽略,但Memento模式的主要作用在于职责的分离,同时隐藏Originator的实现细节,并在有些情况下起到简化Originator的作用。

因为Memento模式具有的优点在很多情况下并不为我们所关系,同时,还会由此引入更复杂的类结构及可能引入更大的管理开销,因此,是否需要引入Memento模式及何时引入Memento模式是一个需要认真考虑的问题,个人认为,Memento模式比较适用于功能比较复杂的,但需要维护或记录属性历史的类,对于这样的类,对Caretaker公开Originator的接口显得有点多余,或者需要保存的属性只是众多属性中的一小部分时(或者根本就不是Originator的属性,但是由Originator引起,并且Originator可以根据保存的Memento信息还原到前一状态,如GoF的DP一书中提到的增量约束解释工具QOCA),为了节约存储空间,也可以考虑使用Memento模式(这种情况下,clone就太奢侈,也太不负责任了,:))。

以下示例取自参考1:

// 1. Assign the roles of "caretaker" and "originator"

// 2. Create a "memento" class and declare the originator a friend

// 3. Caretaker knows when to "check point" the originator

// 4. Originator creates a memento and copies its state to the memento

// 5. Caretaker holds on to (but cannot peek in to) the memento

// 6. Caretaker knows when to "roll back" the originator

// 7. Originator reinstates itself using the saved state in the memento

#include <iostream>

#include <string>

using namespace std;

class Memento { // 2. Create a "memento" class and

friend class Stack; // declare the originator a friend

int *items, num;

Memento( int* arr, int n ) {

items = new int[num = n];

for (int i=0; i < num; i++) items[i] = arr[i]; }

public:

~Memento() { delete items; }

};

class Stack { // 1. Stack is the "originator"

int items[10], sp;

public:

Stack() { sp = -1; }

void push( int in ) { items[++sp] = in; }

int pop() { return items[sp--]; }

bool isEmpty() { return sp == -1; }

// 4. Originator creates a memento and copies its state to the memento

Memento* checkPoint() {

return new Memento( items, sp+1 );

}

// 7. Originator reinstates itself using the saved state in the memento

void rollBack( Memento* m ) {

sp = m->num-1;

for (int i=0; i < m->num; i++) items[i] = m->items[i];

}

friend ostream& operator<< ( ostream& os, const Stack& s ) {

string buf( "[ " );

for (int i=0; i < s.sp+1; i++) { buf += s.items[i]+48; buf += ' '; }

buf += ']';

return os << buf;

}

};

// 1. main() is the "caretaker"

void main( void ) {

Stack s;

int i = 0;

for (i=0; i < 5; i++) s.push( i );

cout << "stack is " << s << endl;

Memento* first = s.checkPoint(); // 3. Caretaker knows when to save

for (i=5; i < 10; i++) s.push( i ); // 5. Caretaker holds on to memento

cout << "stack is " << s << endl;

Memento* second = s.checkPoint(); // 3. Caretaker knows when to save

cout << "popping stack: "; // 5. Caretaker holds on to memento

while ( ! s.isEmpty()) cout << s.pop() << ' '; cout << endl;

cout << "stack is " << s << endl;

s.rollBack( second ); // 6. Caretaker knows when to undo

cout << "second is " << s << endl;

s.rollBack( first ); // 6. Caretaker knows when to undo

cout << "first is " << s << endl;

cout << "popping stack: ";

while ( ! s.isEmpty()) cout << s.pop() << ' '; cout << endl;

delete first; delete second;

}

参考:

1、http://home.earthlink.net/~huston2/dp/MementoDemosCpp

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有