C++ 備忘錄模式 - 開啟月光寶盒

備忘錄模式(Memento Pattern)在不破壞封裝的前提下,捕獲一個對象的內部狀態(tài),并在該對象之外保存這個狀態(tài),這樣可以在以后將對象恢復到原先保存的狀態(tài)。
1
模式結構
UML 結構圖:

Originator(發(fā)起人):負責創(chuàng)建一個 Memento,以記錄當前時刻自身的內部狀態(tài),并可以使用 Memento 恢復內部狀態(tài)。Originator 可以根據(jù)需要決定 Memento 儲存自己的哪些內部狀態(tài)。
Memento(備忘錄):負責存儲 Originator 對象的內部狀態(tài),并可以防止 Originator 以外的其他對象訪問備忘錄。
Caretaker(管理者):負責管理 Memento,但不能對 Memento 的內容進行訪問或者操作。
2
優(yōu)缺點
提供了一種狀態(tài)恢復機制,使用戶能夠方便地回到某個歷史的狀態(tài)。
實現(xiàn)了信息的封裝,使得用戶不需要關心狀態(tài)的保存細節(jié)。
如果 Originator 對象很大,那么 Memento 對象也會很大,這勢必會占用較大的存儲空間,而且每保存一次都需要消耗一定的系統(tǒng)資源。
3
適用場景
保存一個對象在某一個時刻的全部(或部分)狀態(tài),這樣在以后需要時便能夠恢復到先前的狀態(tài),實現(xiàn)撤銷操作。
防止外界對象破壞一個對象歷史狀態(tài)的封裝性,避免將對象歷史狀態(tài)的實現(xiàn)細節(jié)暴露給外界對象。
4
案例分析
月光寶盒 - 讓時光倒流

很多人說年少時看《大話西游》,看著看著就笑了;長大之后再看,看著看著就哭了。無論是笑了還是哭了,心中總有很多疑惑無法解開。
《大話西游》依托于“月光寶盒”,構建了幾個平行時空中發(fā)生的愛情故事。其中,有一個經典橋段:
至尊寶在盤絲洞找到盤絲大仙留下的月光寶盒,此時牛魔王帶白晶晶來到盤絲洞。白晶晶以為春三十娘和二當家生下的孩子是她和至尊寶所生,憤而自刎。至尊寶為了救白晶晶,使用月光寶盒使時光倒流,幾次后產生故障,竟將其帶回五百年前,這時紫霞仙子向他走來 …
和“月光寶盒”一樣,備忘錄模式也提供了時光倒流的機制,將一個對象某個時刻的狀態(tài)進行備份,當用戶后悔(需要返回之前的狀態(tài))時,可以把備份調用出來!
5
代碼實現(xiàn)
穿越至某一時刻,這個時刻指具體的日期時間,用 DateTime 表示,并為其提供相應的 setter 和 getter 方法:
//?memento.h
#ifndef?MEMENTO_H
#define?MEMENTO_H
#include?
#include?
//?日期時間
class?DateTime
{
public:
????DateTime(std::string?dt)?:?m_dateTime(dt)?{}
????~DateTime()?{}
????void?SetDateTime(std::string?dt)?{
????????m_dateTime?=?dt;
????}
????std::string?GetDateTime()?{
????????return?m_dateTime;
????}
private:
????std::string?m_dateTime;
};
#endif?//?MEMENTO_H
Life 用于創(chuàng)建 DateTime,以記錄當前的日期時間,并可以使用 DateTime 進行恢復:
//?originator.h
#ifndef?ORIGINATOR_H
#define?ORIGINATOR_H
#include?"memento.h"
#include?
#include?
//?一生
class?Life
{
public:
????Life()?{}
????~Life()?{}
????void?SetDateTime(std::string?dt)?{
????????std::cout?<"Set?date?time?to?"?<std::endl;
????????m_dateTime?=?dt;
????}
????//?僅用于打印
????std::string?GetDateTime()?{
????????return?m_dateTime;
????}
????//?恢復日期時間
????void?SetMemento(DateTime?*dt)?{
????????if?(nullptr?!=?dt)
????????????m_dateTime?=?dt->GetDateTime();
????}
????//?創(chuàng)建日期時間
????DateTime?*CreateMemento()?{
????????return?new?DateTime(m_dateTime);
????}
private:
????std::string?m_dateTime;
};
#endif?//?ORIGINATOR_H
這是時光倒流的關鍵,通過 PandoraBox,至尊寶才可以彌補遺憾:
//?originator.h
#ifndef?CARE_TAKER_H
#define?CARE_TAKER_H
#include?"originator.h"
#include?
//?月光寶盒
class?PandoraBox
{
public:
????PandoraBox(Life?*life)?:?m_pLife(life)?{}
????~PandoraBox()?{
????????for?(auto?itr?=?m_history.begin();?itr?!=?m_history.end();?++itr)?{
????????????delete?*itr;
????????}
????????m_history.clear();
????}
????//?保存?zhèn)浞?/span>
????void?Save()?{
????????m_history.push_back(m_pLife->CreateMemento());
????}
????//?穿越至某一時刻(隨機取時間)
????void?Undo()?{
????????int?index?=?rand()?%?m_history.size();
????????m_pLife->SetMemento(m_history[index]);
????}
private:
????Life?*m_pLife;
????std::vector?m_history;
};
#endif?//?CARE_TAKER_H
想啟動“月光寶盒”,體驗一把穿越的感覺?大聲喊出那句爛熟于心的“般bō若rě波羅蜜”:
//?main.cpp
#include?"originator.h"
#include?"care_taker.h"
#include?
#include?
#ifndef?SAFE_DELETE
#define?SAFE_DELETE(p)?{?if(p){delete(p);?(p)=NULL;}?}
#endif
int?main()
{
????srand((unsigned)time(0));
????Life?*life?=?new?Life();
????PandoraBox?*box?=?new?PandoraBox(life);
????//?設置并保存一些歷史時間
????for?(int?i?=?0;?i?5;?i++)?{
????????std::ostringstream?buffer;
????????buffer?<"200"?<"/10/01?00:00:00";
????????std::string?dt?=?buffer.str();
????????life->SetDateTime(dt);
????????box->Save();
????}
????//?穿越多次
????for?(int?i?=?0;?i?2;?i++)?{
????????box->Undo();
????????std::cout?<"Current?date?time?is?"?<GetDateTime()?<std::endl;
????}
????SAFE_DELETE(life);
????SAFE_DELETE(box);
????getchar();
????return?0;
}
輸出如下:
Set date time to 2000/10/01 00:00:00
Set date time to 2001/10/01 00:00:00
Set date time to 2002/10/01 00:00:00
Set date time to 2003/10/01 00:00:00
Set date time to 2004/10/01 00:00:00
Current date time is 2001/10/01 00:00:00
Current date time is 2003/10/01 00:00:00
·END·

