C++設(shè)計(jì)模式之備忘錄模式
前言
又到年底了,也靜不下心來(lái)寫(xiě)代碼了,大家都很浮躁;翻出經(jīng)典的《仙劍奇?zhèn)b傳》玩一會(huì);又要打大BOSS,先存一下檔吧。這是我的習(xí)慣,在打大BOSS之前,都要先存一下檔,要是打贏了,就再存一個(gè)檔,覆蓋之前的;如果打輸了,就恢復(fù)之前的存檔,接著重來(lái)。我想大家都是這么玩的吧。哎呀,總是打不過(guò)。好了,不玩了,但是,游戲中的那個(gè)存檔行為卻讓我很著迷,它是如何實(shí)現(xiàn)的呢?帶著好奇的心,去百度了一下;哦,原來(lái)如此。好吧,開(kāi)始今天的總結(jié)吧——備忘錄模式。
備忘錄模式
在GOF的《設(shè)計(jì)模式:可復(fù)用面向?qū)ο筌浖幕A(chǔ)》一書(shū)中對(duì)備忘錄模式是這樣說(shuō)的:在不破壞封裝性的前提下,捕獲一個(gè)對(duì)象的內(nèi)部狀態(tài),并在該對(duì)象之外保存這個(gè)狀態(tài)。這樣以后就可將該對(duì)象恢復(fù)到原先保存的狀態(tài)。
有時(shí)有必要記錄一個(gè)對(duì)象的內(nèi)部狀態(tài)。為了允許用戶取消不確定的操作或從錯(cuò)誤中恢復(fù)過(guò)來(lái),需要實(shí)現(xiàn)檢查點(diǎn)和取消機(jī)制,而要實(shí)現(xiàn)這些機(jī)制,你必須事先將狀態(tài)信息保存在某處,這樣才能將對(duì)象恢復(fù)到它們先前的狀態(tài)。如何實(shí)現(xiàn)這個(gè)將狀態(tài)信息保存在某處呢?使用原型模式?由于對(duì)象通常封裝了其部分或所有的狀態(tài)信息,使得其狀態(tài)不能被其他對(duì)象訪問(wèn),也就不可能在該對(duì)象之外保存其狀態(tài)了。由于原型模式總是返回對(duì)象的全部狀態(tài)信息,同時(shí)原型模式使其狀態(tài)能被其它對(duì)象訪問(wèn),這樣就違反了封裝的原則,還可能有損應(yīng)用的可靠性和可擴(kuò)展性。
再拿上面的《仙劍奇?zhèn)b傳》進(jìn)行分析,當(dāng)我們?cè)诖虼驜OSS之前存檔,此時(shí)就需要將對(duì)應(yīng)的游戲場(chǎng)景,任務(wù)信息,人物信息等等狀態(tài)存儲(chǔ)起來(lái);當(dāng)贏得大BOSS之后,覆蓋之前的存檔時(shí),就將之前的存檔丟棄,新建立一個(gè)存檔,保存當(dāng)前的狀態(tài)信息;如果打輸了,恢復(fù)存檔,就將之前的存檔信息讀取出來(lái),還原到打大BOSS之前的游戲場(chǎng)景,重新開(kāi)始打大BOSS。這里面就是使用的備忘錄模式。
一個(gè)備忘錄是一個(gè)對(duì)象,它存儲(chǔ)另一個(gè)對(duì)象在某個(gè)瞬間的內(nèi)部狀態(tài),而后者稱(chēng)為備忘錄的原發(fā)器。當(dāng)需要設(shè)置原發(fā)器的檢查點(diǎn)時(shí),取消操作機(jī)制會(huì)向原發(fā)器請(qǐng)求一個(gè)備忘錄。原發(fā)器用描述當(dāng)前狀態(tài)的信息初始化該備忘錄。只有原發(fā)器可以向備忘錄中存取信息,備忘錄對(duì)其他的對(duì)象是“不可見(jiàn)”的。
UML類(lèi)圖
Memento:備忘錄存儲(chǔ)原發(fā)器對(duì)象的內(nèi)部狀態(tài)。原發(fā)器根據(jù)需要決定備忘錄存儲(chǔ)原發(fā)器的哪些內(nèi)部狀態(tài);防止原發(fā)器以外的其他對(duì)象訪問(wèn)備忘錄。備忘錄實(shí)際上有兩個(gè)接口,管理者只能看到備忘錄的窄接口————它只能將備忘錄傳遞給其他對(duì)象。相反,原發(fā)器能夠看到一個(gè)寬接口,允許它訪問(wèn)返回到先前狀態(tài)所需的所有數(shù)據(jù)。理想的情況是只允許生成備忘錄的那個(gè)原發(fā)器訪問(wèn)本備忘錄的內(nèi)部狀態(tài);
Originator:原發(fā)器創(chuàng)建一個(gè)備忘錄,用以記錄當(dāng)前時(shí)刻它的內(nèi)部狀態(tài);我們使用備忘錄恢復(fù)內(nèi)部狀態(tài);
Caretaker:負(fù)責(zé)保存好備忘錄;但是,不能對(duì)備忘錄的內(nèi)容進(jìn)行操作或檢查。
備忘錄模式是按照以下方式進(jìn)行協(xié)作的:
管理器向原發(fā)器請(qǐng)求一個(gè)備忘錄,保留一段時(shí)間后,將其送回給原發(fā)器;而有的時(shí)候管理者不會(huì)將備忘錄返回給原發(fā)器,因?yàn)樵l(fā)器可能根本不需要退到先前的狀態(tài)。備忘錄是被動(dòng)的,只有創(chuàng)建備忘錄的原發(fā)器會(huì)對(duì)它的狀態(tài)進(jìn)行賦值和檢索,如下面的時(shí)序圖:
使用場(chǎng)合
在以下情況下使用備忘錄模式:
1.必須保存一個(gè)對(duì)象在某一個(gè)時(shí)刻的部分或完整狀態(tài),這樣以后需要時(shí)它才能恢復(fù)到先前的狀態(tài);
2.如果一個(gè)用接口來(lái)讓其它對(duì)象直接得到這些狀態(tài),將會(huì)暴露對(duì)象的實(shí)現(xiàn)細(xì)節(jié)并破壞對(duì)象的封裝性。
代碼實(shí)現(xiàn):
#include <iostream>
using namespace std;
struct State
{
wchar_t wcsState[260];
};
class Memento
{
public:
Memento(State *pState) : m_pState(pState){}
State *GetState() { return m_pState; }
private:
friend class Originator;
State *m_pState;
};
class Originator
{
public:
Originator() : m_pState(NULL) {}
~Originator()
{
// Delete the storage of the state
if (m_pState)
{
delete m_pState;
m_pState = NULL;
}
}
void SetMemento(Memento *pMemento);
Memento *CreateMemento();
void SetValue(wchar_t *value)
{
memset(wcsValue, 0, 260 * sizeof(wchar_t));
wcscpy_s(wcsValue, 260, value);
}
void PrintState() { wcout<<wcsValue<<endl; }
private:
State *m_pState; // To store the object's state
wchar_t wcsValue[260]; // This is the object's real data
};
Memento *Originator::CreateMemento()
{
m_pState = new State;
if (m_pState == NULL)
{
return NULL;
}
Memento *pMemento = new Memento(m_pState);
wcscpy_s(m_pState->wcsState, 260, wcsValue); // Backup the value
return pMemento;
}
void Originator::SetMemento(Memento *pMemento)
{
m_pState = pMemento->GetState();
// Recovery the data
memset(wcsValue, 0, 260 * sizeof(wchar_t));
wcscpy_s(wcsValue, 260, m_pState->wcsState);
}
// Manager the Memento
class Caretaker
{
public:
Memento *GetMemento() { return m_pMemento; }
void SetMemnto(Memento *pMemento)
{
// Free the previous Memento
if (m_pMemento)
{
delete m_pMemento;
m_pMemento = NULL;
}
// Set the new Memento
m_pMemento = pMemento;
}
private:
Memento *m_pMemento;
};
int main()
{
Originator *pOriginator = new Originator();
pOriginator->SetValue(L"On");
pOriginator->PrintState();
// Now I backup the state
Caretaker *pCaretaker = new Caretaker();
pCaretaker->SetMemnto(pOriginator->CreateMemento());
// Set the new state
pOriginator->SetValue(L"Off");
pOriginator->PrintState();
// Recovery to the old state
pOriginator->SetMemento(pCaretaker->GetMemento());
pOriginator->PrintState();
if (pCaretaker)
{
delete pCaretaker;
}
if (pOriginator)
{
delete pOriginator;
}
return 0;
}
我再根據(jù)上面的《仙劍奇?zhèn)b傳》來(lái)完成備忘錄模式,代碼如下:
#include <iostream>
using namespace std;
class RoleStateMemento
{
public:
RoleStateMemento(unsigned iBlood, unsigned iAttack, unsigned iDefense) : m_iBlood(iBlood), m_iAttack(iAttack), m_iDefense(iDefense){}
private:
friend class GameRole;
unsigned GetBloodValue() { return m_iBlood; }
unsigned GetAttackValue() { return m_iAttack; }
unsigned GetDefenseValue() { return m_iDefense; }
unsigned m_iBlood; // 生命力
unsigned m_iAttack; // 攻擊力
unsigned m_iDefense; // 防御力
};
class GameRole
{
public:
GameRole() : m_iBlood(100), m_iAttack(100), m_iDefense(100){}
// 存檔
RoleStateMemento *SaveState() { return new RoleStateMemento(m_iBlood, m_iAttack, m_iDefense); }
// 恢復(fù)存檔
void RecoveryState(RoleStateMemento *pRoleState)
{
m_iBlood = pRoleState->GetBloodValue();
m_iAttack = pRoleState->GetAttackValue();
m_iDefense = pRoleState->GetDefenseValue();
cout<<"Recovery..."<<endl;
}
void ShowState()
{
cout<<"Blood:"<<m_iBlood<<endl;
cout<<"Attack:"<<m_iAttack<<endl;
cout<<"Defense:"<<m_iDefense<<endl;
}
void Fight()
{
m_iBlood -= 100;
m_iAttack -= 10;
m_iDefense -= 20;
if (m_iBlood == 0)
{
cout<<"Game Over"<<endl;
}
}
private:
unsigned m_iBlood; // 生命力
unsigned m_iAttack; // 攻擊力
unsigned m_iDefense; // 防御力
};
class RoleStateCaretaker
{
public:
void SetRoleStateMemento(RoleStateMemento *pRoleStateMemento) { m_pRoleStateMemento = pRoleStateMemento; }
RoleStateMemento *GetRoleStateMemento() { return m_pRoleStateMemento; }
private:
RoleStateMemento *m_pRoleStateMemento;
};
int main()
{
GameRole *pLiXY = new GameRole(); // 創(chuàng)建李逍遙這個(gè)角色
pLiXY->ShowState(); // 顯示初始的狀態(tài)
// 存檔
RoleStateCaretaker *pRoleStateCaretaker = new RoleStateCaretaker();
pRoleStateCaretaker->SetRoleStateMemento(pLiXY->SaveState());
// 開(kāi)始打大BOSS
pLiXY->Fight();
pLiXY->ShowState();
// 讀檔,從新開(kāi)始
pLiXY->RecoveryState(pRoleStateCaretaker->GetRoleStateMemento());
pLiXY->ShowState();
return 0;
}
總結(jié)
備忘錄模式在實(shí)際應(yīng)用中也不少;我們?cè)谶M(jìn)行文檔編輯時(shí),經(jīng)常使用的撤銷(xiāo)操作。使用C++實(shí)現(xiàn)備忘錄模式的關(guān)鍵點(diǎn)在于Originator類(lèi)是Memento的友元類(lèi),這樣就使得管理備忘錄的Caretaker對(duì)象,以及其它對(duì)象都不能讀取、設(shè)置備忘錄,只有Originator類(lèi)才能進(jìn)行備忘錄的讀取和設(shè)置。由于備忘錄主要是用于對(duì)對(duì)象的狀態(tài)進(jìn)行備份,實(shí)現(xiàn)了撤銷(xiāo)操作,如果對(duì)象的狀態(tài)數(shù)據(jù)很大很多時(shí),在進(jìn)行備忘時(shí),就會(huì)很占用資源,這個(gè)是我們?cè)趯?shí)際開(kāi)發(fā)時(shí)需要考慮的東西。結(jié)合之前的設(shè)計(jì)模式,在總結(jié)命令模式時(shí),說(shuō)到命令模式支持事物的回退,而這個(gè)就是依靠的備忘錄模式來(lái)實(shí)現(xiàn)的。好了,備忘錄模式就總結(jié)至此。希望對(duì)大家有用。
上一篇:C++歸并排序算法實(shí)例
欄 目:C語(yǔ)言
本文標(biāo)題:C++設(shè)計(jì)模式之備忘錄模式
本文地址:http://mengdiqiu.com.cn/a1/Cyuyan/3291.html
您可能感興趣的文章
- 04-02c語(yǔ)言沒(méi)有round函數(shù) round c語(yǔ)言
- 01-10深入理解C++中常見(jiàn)的關(guān)鍵字含義
- 01-10使用C++實(shí)現(xiàn)全排列算法的方法詳解
- 01-10APUE筆記之:進(jìn)程環(huán)境詳解
- 01-10c++中inline的用法分析
- 01-10用C++實(shí)現(xiàn)DBSCAN聚類(lèi)算法
- 01-10全排列算法的非遞歸實(shí)現(xiàn)與遞歸實(shí)現(xiàn)的方法(C++)
- 01-10C++大數(shù)模板(推薦)
- 01-10淺談C/C++中的static與extern關(guān)鍵字的使用詳解
- 01-10深入C/C++浮點(diǎn)數(shù)在內(nèi)存中的存儲(chǔ)方式詳解


閱讀排行
- 1C語(yǔ)言 while語(yǔ)句的用法詳解
- 2java 實(shí)現(xiàn)簡(jiǎn)單圣誕樹(shù)的示例代碼(圣誕
- 3利用C語(yǔ)言實(shí)現(xiàn)“百馬百擔(dān)”問(wèn)題方法
- 4C語(yǔ)言中計(jì)算正弦的相關(guān)函數(shù)總結(jié)
- 5c語(yǔ)言計(jì)算三角形面積代碼
- 6什么是 WSH(腳本宿主)的詳細(xì)解釋
- 7C++ 中隨機(jī)函數(shù)random函數(shù)的使用方法
- 8正則表達(dá)式匹配各種特殊字符
- 9C語(yǔ)言十進(jìn)制轉(zhuǎn)二進(jìn)制代碼實(shí)例
- 10C語(yǔ)言查找數(shù)組里數(shù)字重復(fù)次數(shù)的方法
本欄相關(guān)
- 04-02c語(yǔ)言函數(shù)調(diào)用后清空內(nèi)存 c語(yǔ)言調(diào)用
- 04-02func函數(shù)+在C語(yǔ)言 func函數(shù)在c語(yǔ)言中
- 04-02c語(yǔ)言的正則匹配函數(shù) c語(yǔ)言正則表達(dá)
- 04-02c語(yǔ)言用函數(shù)寫(xiě)分段 用c語(yǔ)言表示分段
- 04-02c語(yǔ)言中對(duì)數(shù)函數(shù)的表達(dá)式 c語(yǔ)言中對(duì)
- 04-02c語(yǔ)言編寫(xiě)函數(shù)冒泡排序 c語(yǔ)言冒泡排
- 04-02c語(yǔ)言沒(méi)有round函數(shù) round c語(yǔ)言
- 04-02c語(yǔ)言分段函數(shù)怎么求 用c語(yǔ)言求分段
- 04-02C語(yǔ)言中怎么打出三角函數(shù) c語(yǔ)言中怎
- 04-02c語(yǔ)言調(diào)用函數(shù)求fibo C語(yǔ)言調(diào)用函數(shù)求
隨機(jī)閱讀
- 08-05織夢(mèng)dedecms什么時(shí)候用欄目交叉功能?
- 01-10SublimeText編譯C開(kāi)發(fā)環(huán)境設(shè)置
- 01-11Mac OSX 打開(kāi)原生自帶讀寫(xiě)NTFS功能(圖文
- 01-10delphi制作wav文件的方法
- 01-11ajax實(shí)現(xiàn)頁(yè)面的局部加載
- 08-05DEDE織夢(mèng)data目錄下的sessions文件夾有什
- 01-10C#中split用法實(shí)例總結(jié)
- 04-02jquery與jsp,用jquery
- 01-10使用C語(yǔ)言求解撲克牌的順子及n個(gè)骰子
- 08-05dedecms(織夢(mèng))副欄目數(shù)量限制代碼修改