生活中大家可能都幻想过如果当初我没怎么样怎么样就好了,当产生这样的想法的时候一定是发生什么不如意的事。生活是现实的,时间也永远不会回到做错事之前的一刹那。软件虽服务于生活,模拟生活却也和生活有所不同,应用软件时,我们可以比较容易的进行备忘,比如编辑word或者txt时,我们使用的快捷键ctrl+z。还有我们可能也对系统进行过备份吧~~~
1.初识备忘录模式
实际上在软件中备忘录模式的使用时非常频繁的,并且备忘录模式又是比较简单的一种设计模式,所以可以说是性价比很高。比如我们玩中国象棋的时候需要悔棋,浏览网页时需要后退等等这些都是备忘录模式的应用。但在这里我们想说的是关于游戏进度保存的问题。当我们玩一款单机游戏的时候,可能我们突然有什么事要关机出去一段时间,或者好不容打到关底了,但是boss打不过还要重玩,这是我们可能也希望在打最终boss之前保存一下,这些时候可能我们都希望使用备忘录模式,利用备忘录模式保存打boss之前的状态,如果打boss不过呢,还可以恢复进度,从打boss之前的状态开始重打~~~
Gof对备忘录模式是这样说的:
备忘录模式(Memento):在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样以后就可将该对象恢复到原先保存的状态。
同样的我们还看看备忘录模式的结构图:
从结构中看出备忘录模式包含3个角色:
1.Originator(发起人):负责创建一个备忘录Memento,用以记录当前时刻自身的内部状态,并可使用备忘录恢复内部状态。Originator可以根据需要决定Memento存储自己的哪些内部状态。
2.Memento(备忘录):负责存储Originator对象的内部状态,并可以防止Originator以外的其他对象访问备忘录。备忘录有两个接口:Caretaker只能看到备忘录的窄接口,他只能将备忘录传递给其他对象。Originator却可看到备忘录的宽接口,允许它访问返回到先前状态所需要的所有数据。
3.Caretaker(管理者):负责备忘录Memento,不能对Memento的内容进行访问或者操作。
现在假设游戏角色中有两个属性,角色名称和角色生命值,现在要进行的保存游戏角色的生命值,但是游戏角色名称不用保存。我们来看看怎样利用备忘录模式的代码实现:
1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 //角色备忘录类 7 class RoleStateMemento 8 { 9 private: 10 int vitality; 11 public: 12 RoleStateMemento(){}; 13 RoleStateMemento(int vit) 14 { 15 this->vitality = vit; 16 } 17 int get_vitality() 18 { 19 return vitality; 20 } 21 }; 22 23 //游戏角色类 24 class GameRole 25 { 26 private: 27 int vitality; 28 string name; 29 public: 30 GameRole(int Vit,string Name) 31 { 32 this->vitality = Vit; 33 this->name = Name; 34 } 35 //保存进度,只保存了生命力(vitality) 36 RoleStateMemento save_state() 37 { 38 RoleStateMemento memento(vitality); 39 return memento; 40 } 41 //恢复进度 42 void role_state_recover(RoleStateMemento memento) 43 { 44 this->vitality = memento.get_vitality(); 45 } 46 //展示游戏角色状态 47 void show() 48 { 49 cout<<"游戏角色名称: "<<name<<endl; 50 cout<<"游戏角色生命值: "<<vitality<<endl; 51 } 52 //大战boss生命值变为0 53 void fight() 54 { 55 this->vitality = 0; 56 } 57 }; 58 //角色状态管理者类 59 class RoleStateCaretaker 60 { 61 private: 62 RoleStateMemento memento; 63 public: 64 void set_memento(RoleStateMemento mem) 65 { 66 this->memento = mem; 67 } 68 RoleStateMemento get_memento() 69 { 70 return this->memento; 71 } 72 }; 73 74 int main() 75 { 76 //大战boss前 77 GameRole Vincent(100,"VincentChen"); 78 Vincent.show(); 79 //保存进度 80 RoleStateCaretaker StateManager; 81 StateManager.set_memento(Vincent.save_state()); 82 //大战boss 83 Vincent.fight(); 84 cout<<endl<<"大战boss后的状态:"<<endl; 85 Vincent.show(); 86 //恢复进度 87 Vincent.role_state_recover(StateManager.get_memento()); 88 cout<<endl<<"恢复进度后的状态:"<<endl; 89 Vincent.show(); 90 return 0; 91 }
运行结果:
小结:想一想如果不用备忘录模式怎么实现呢?如果要保存进度就要在main函数(客户端)中保存Vincent的vitality属性的值,然后恢复进度就要将Vincent的citality属性赋值为已经保存下来的那个值,这样一来的话,main函数(客户端)要知道GameRole类内部的细节,并且要完成的操作也很多,这时如果游戏角色加入一些要保存的属性更改起来也不方便。想想备忘录模式的好处吧~~~
3.使用备忘录模式的好处和优缺点
使用备忘录模式的场合:
(1)功能比较复杂的,但是需要维护或记录属性历史的类。
(2)需要保存的属性只是众多属性的一小部分时。
使用备忘录模式的好处:
(1)有时一些发起人对象的内部信息必须保存在发起人对象以外的地方,但是必须要由发起人对象自己读取,这时使用备忘录模式可以把复杂的发起人内部信息对其他的对象屏蔽起来,从而可以恰当地保持封装的边界。
(2)本模式简化了发起人类。发起人不再需要管理和保存其内部状态的一个个版本,客户端可以自行管理他们所需要的这些状态的版本。
(3)当发起人角色的状态改变的时候,有可能这个状态无效,这时候就可以使用暂时存储起来的备忘录将状态复原。
使用备忘录模式的缺点:
(1)如果发起人角色的状态需要完整地存储到备忘录对象中,那么在资源消耗上面备忘录对象会很昂贵。
(2)当负责人角色将一个备忘录存储起来的时候,负责人可能并不知道这个状态会占用多大的存储空间,从而无法提醒用户一个操作是否很昂贵。