第十八章 如果再回到从前-备忘录模式
18.1 如果再给我一次机会
玩单机游戏的时候,我们都是在打大BOSS之前先保存一个进度,然后如果通关失败了,可以再返回刚才那个进度来从头来过。这里就会引入备忘录模式。
18.2 游戏存进度
游戏角色类,用来存储角色的生命力,攻击力,防御力的数据。但是在这个程序中,代码没有错但未必优。
GameRole.h
#pragma once #include <iostream> class GameRole { public: int GetVitality(void) { return m_vit; }; void SetVitality(int vit) { m_vit = vit; }; int GetAttack(void) { return m_atk; }; void SetAttack(int atk) { m_atk = atk; }; int GetDefense(void) { return m_def; }; void SetDefense(int def) { m_def = def; }; void StateDisplay(void) { std::cout << "栶偺忬懺:\n" << "懱椡:" << m_vit << "\n" << "峌寕椡:" << m_atk << "\n" << "杊屼椡:" << m_def << std::endl; }; void GetInitState(void) { m_vit = 100; m_atk = 100; m_def = 100; }; void Fight(void) { m_vit = 0; m_atk = 0; m_def = 0; }; private: int m_vit; int m_atk; int m_def; };
#include "GameRole.h" int _tmain(int argc, _TCHAR* argv[]) { GameRole* lixiaoyao = new GameRole(); lixiaoyao->GetInitState(); lixiaoyao->StateDisplay(); GameRole* backup = new GameRole(); backup->SetAttack(lixiaoyao->GetAttack()); backup->SetDefense(lixiaoyao->GetDefense()); backup->SetVitality(lixiaoyao->GetVitality()); lixiaoyao->Fight(); lixiaoyao->StateDisplay(); lixiaoyao->SetAttack(backup->GetAttack()); lixiaoyao->SetDefense(backup->GetDefense()); lixiaoyao->SetVitality(backup->GetVitality()); lixiaoyao->StateDisplay(); delete lixiaoyao; delete backup; return 0; }
在这块代码中,暴露了实现的细节,所以不足取。
18.3 备忘录模式
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。
Originator(发起人):负责创建一个备忘录Memento,用以记录当前时刻他的内部状态,并可使用备忘录恢复内部状态。Originator可根据需要决定Memento存储Originator的那些内部状态。
Memento(备忘录):负责存储Originator对象的内部状态,并可防止Originator以外的其它对象访问备忘录Memento。
Caretaker(管理者):负责保存好备忘录Memento,不能对备忘录的内容进行操作或检查。
18.4 备忘录模式基本代码
Caretaker.h
#pragma once #include "Memento.h" class Caretaker { public: void SetMemento(Memento* p) { m_pMemento = p; }; Memento* GetMemento(void) { return m_pMemento; }; ~Caretaker() { delete m_pMemento; }; private: Memento* m_pMemento; };
Memento.h
#pragma once #include <iostream> #include <string> class Memento { public: Memento(std::string state) { m_State = state; }; std::string GetState(void) { return m_State; }; private: std::string m_State; };
Originator.h
#pragma once #include "Memento.h" class Originator { public: void SetState(std::string state) { m_State = state; }; std::string GetState(void) { return m_State; }; Memento* CreateMemento() { return new Memento(m_State); }; void SetMemento(Memento* p) { m_State = p->GetState(); }; void Show() { std ::cout << "State=" << m_State << std::endl; }; private: std::string m_State; };
客户端程序
#include "stdafx.h" #include "Caretaker.h" #include "Originator.h" int _tmain(int argc, _TCHAR* argv[]) { Originator* o = new Originator(); o->SetState("On"); o->Show(); Caretaker* c = new Caretaker(); c->SetMemento(o->CreateMemento()); o->SetState("OFF"); o->Show(); o->SetMemento(c->GetMemento()); o->Show(); return 0; }
把要保存的细节给封装在了Memento中了,哪一天要更改保存的细节也不用影响客户端了。备忘录模式比较适用于功能比较复杂的,但需要维护或记录属性历史的类,或者需要保存的属性只是众多属性中的一小部分时,Originator可以根据保存的Memento信息还原到前一状态。
如果在某个系统中使用命令模式时,需要实现命令的撤销功能,那么命令模式可以使用备忘录模式来存储可撤销操作的状态。有时一些对象的内部信息必须保存在对象以外的地方,但是必须要由对象自己读取,这时候使用备忘录可以把复杂的对象内部信息对其它的对象屏蔽起来,从而可以恰当地保持封装的边界。
当角色的状态改变的时候,有可能这个状态无效,这时候就可以使用暂时存储起来的备忘录将状态复原。
18.5 游戏进度备忘
游戏角色类
#pragma once #include "RoleStateMemento.h" #include <iostream> class GameRole { public: RoleStateMemento* SaveState(void) { return new RoleStateMemento(m_vit, m_atk, m_def); }; void RecoveryState(RoleStateMemento* p) { m_vit = p->GetVitality(); m_atk = p->GetAttack(); m_def = p->GetDefense(); }; void StateDisplay(void) { std::cout << "栶偺忬懺:\n" << "懱椡:" << m_vit << "\n" << "峌寕椡:" << m_atk << "\n" << "杊屼椡:" << m_def << std::endl; }; void GetInitState(void) { m_vit = 100; m_atk = 100; m_def = 100; }; void Fight(void) { m_vit = 0; m_atk = 0; m_def = 0; }; private: int m_vit; int m_atk; int m_def; };
角色状态管理者类
#pragma once #include "RoleStateMemento.h" class RoleStateCaretaker { public: RoleStateMemento* GetRoleStateMemento() { return m_pRoleStateMemento; }; void SetRoleStateMemento(RoleStateMemento* p) { m_pRoleStateMemento = p; }; RoleStateCaretaker() :m_pRoleStateMemento(NULL) { }; ~RoleStateCaretaker() { delete m_pRoleStateMemento; m_pRoleStateMemento = NULL; }; private: RoleStateMemento* m_pRoleStateMemento; };
角色状态存储箱类
#pragma once class RoleStateMemento { public: RoleStateMemento(int vit, int atk, int def) { m_vit = vit; m_atk = atk; m_def = def; }; int GetVitality(void) { return m_vit; }; void SetVitality(int vit) { m_vit = vit; }; int GetAttack(void) { return m_atk; }; void SetAttack(int atk) { m_atk = atk; }; int GetDefense(void) { return m_def; }; void SetDefense(int def) { m_def = def; }; private: int m_vit; int m_atk; int m_def; };