天天看點

C++設計模式——備忘錄模式(memento pattern)一、原理講解二、實作代碼三、總結四、參考内容

部落客看了許多文章和一些書,發現要麼代碼不全,要麼對備忘錄的了解有偏差,要麼幹脆根本就不是備忘錄模式,經過部落客查閱十餘篇文章和詳細研究書本備忘錄模式,總結出這篇精華的c++備忘錄模式文章,感興趣的朋友可以閱讀和提出建議。

一、原理講解

别名Token。

1.1意圖

在不破壞封裝性的前提下,捕獲一個對象的内部狀态,并在該對象之外儲存這個狀态。這樣以後就可以将該對象恢複到原先儲存的狀态。

1.2應用場景

  • 必須儲存一個對象在某時刻的(部分)狀态,這樣以後需要時它才能恢複到先前的狀态;
  • 如果一個接口讓其它對象直接得到這些狀态,将會暴露對象的實作細節并且破壞對象的封裝性;

1.3結構圖(UML圖)

C++設計模式——備忘錄模式(memento pattern)一、原理講解二、實作代碼三、總結四、參考内容

1.4參與者

1.4.1備忘錄(Memento)

  • 存儲原發器對象的内部狀态。原發器根據需要決定備忘錄存儲原發器的哪些内部狀态;
  • 防止原發器以外的其他對象通路備忘錄。備忘錄實際上有兩個接口,管理者(caretaker)隻能看到備忘錄的窄接口(它隻能将備忘錄傳遞給其它對象)。相反,原發器能夠看到一個寬接口,允許它通路傳回到先前狀态所需的所有資料。理想情況隻允許生成備忘錄那個原發器通路本備忘錄;

1.4.2原發器(Originator)

  • 建立一個備忘錄,記錄它的目前時刻得内部狀态;
  • 使用備忘錄恢複内部狀态;

1.4.3管理者(caretaker)

  • 負責儲存好備忘錄;
  • 不能對備忘錄的内容進行操作或者檢查;

1.5代碼實作步驟

a1 定義一個備忘錄類Memento,定義兩個接口函數getState()和setState();

a2 定義一個原發器類Originator,定義兩個接口函數createMemento()和setMemento();前一個函數被管理者類的存儲函數調用Caretaker::save(),後一個函數被管理者類的恢複函數調用Caretaker::undo();

a3 定義一個管理者類Caretaker(),定義兩個接口函數save()和undo();前一個函數用于存儲對象目前狀态,後一個函數用于對象恢複某一時刻狀态;

二、實作代碼

2.1執行個體簡單闡述

本文為了簡化代碼,凸顯出狀态模式代碼思想和思路,用std::sting state為對象,儲存對象state目前狀态和恢複對象state最開始狀态,具體可以檢視下面代碼實作。

2.2具體執行個體代碼

具體的實作由兩個版本,一個将Originator聲明為Memnto的友元類,另一個是Memnto直接提供公共接口以供窄接口調用。

2.2.1版本1 黑盒模式:将Originator聲明為Memnto的友元類,Memnto接口聲明為私有

MementoPattern.cpp

#include <iostream>
#include <string>
#include <vector>

using namespace std;

#define DELETE(pointer) delete (pointer); (pointer)=nullptr

class Originator;

class Memento // 備忘錄類
{
public:
	~Memento(){}
	// 提供窄接口,以供管理者類Caretaker調用
private: // 所有接口和變量聲明為私有,目的是為了隻提供給産生這個備忘錄的原發器Originator調用,其它接口無法直接調用
	friend Originator; // 目的是為了通路Memnto的private和protected成員函數和成員變量
	string state;
	Memento(string state) :state(state) {}
	const string getState() const { return state; }
	void setState(string state) { this->state = state; }
};

class Originator // 原發器類
{
	string state;
public:
	Originator(string state) :state(state){}
	Memento* createMemento() { return new Memento(state); } // 傳回對象state目前狀态
	void setMemento(Memento *memento) { state = memento->getState(); } // 設定對象state傳回某一時刻狀态
	void changeState(string state) { this->state = state; } // 對象state的狀态發生改變後需要儲存
	const void showState()const { cout << "state==" << state << endl; }
};

class Caretaker // 管理者類
{
	vector<Memento*> mementos; // 不需要在參數清單初始化
	Originator *originator;
public:
	Caretaker(Originator *originator) : originator(originator){}
	~Caretaker() { // 析構時手動删除vector容器指針指向的記憶體
		for (auto it = mementos.begin(); it != mementos.end(); ++it) {
			if ((*it) != nullptr) {
				DELETE (*it);
			}
		}
	}
	void save(Memento *memento) { mementos.push_back(memento); } // 記錄備忘錄某一時刻的狀态
	void undo(int index) { originator->setMemento(mementos.at(index)); } // 傳回備忘錄某個時刻index的狀态
};

void doMementoPattern()
{
	string state{ "初始狀态" };
	Originator *originator = new Originator(state);
	Caretaker *caretaker = new Caretaker(originator);
	caretaker->save(originator->createMemento()); // 儲存初始狀态
	originator->showState(); // 列印初始狀态

	originator->changeState(string{ "狀态1" }); // 狀态發生改變1
	caretaker->save(originator->createMemento());
	originator->showState();

	originator->changeState(string{ "狀态2" }); // 狀态發生改變2
	caretaker->save(originator->createMemento());
	originator->showState();
	
	caretaker->undo(0); // 傳回到最初狀态
	originator->showState();

	DELETE(caretaker);
	DELETE(originator);
}
           

main.cpp

#include <iostream>

extern void doMementoPattern();

int main()
{
	doMementoPattern();

	system("pause");
	return 1;
}
           

![版本1輸出結果圖]

C++設計模式——備忘錄模式(memento pattern)一、原理講解二、實作代碼三、總結四、參考内容

2.2.2版本2 白盒模式:将Memnto接口聲明為公有

MementoPattern.cpp

#include <iostream>
#include <string>
#include <vector>

using namespace std;

#define DELETE(pointer) delete (pointer); (pointer)=nullptr

class Memento // 備忘錄類
{
	string state;
public:
	Memento(string state) :state(state) {}
	const string getState() const { return state; }
	void setState(string state) { this->state = state; }
};

class Originator // 原發器類
{
	string state;
public:
	Originator(string state) :state(state){}
	Memento* createMemento() { return new Memento(state); } // 傳回對象state目前狀态
	void setMemento(Memento *memento) { state = memento->getState(); } // 設定對象state傳回某一時刻狀态
	void changeState(string state) { this->state = state; } // 對象state的狀态發生改變後需要儲存
	const void showState()const { cout << "state==" << state << endl; }
};

class Caretaker // 管理者類
{
	vector<Memento*> mementos; // 不需要在參數清單初始化
	Originator *originator;
public:
	Caretaker(Originator *originator) : originator(originator){}
	~Caretaker() { // 析構時手動删除vector容器指針指向的記憶體
		for (auto it = mementos.begin(); it != mementos.end(); ++it) {
			if ((*it) != nullptr) {
				DELETE (*it);
			}
		}
	}
	void save(Memento *memento) { mementos.push_back(memento); } // 記錄備忘錄某一時刻的狀态
	void undo(int index) { originator->setMemento(mementos.at(index)); } // 傳回備忘錄某個時刻index的狀态
};

void doMementoPattern()
{
	string state{ "初始狀态" };
	Originator *originator = new Originator(state);
	Caretaker *caretaker = new Caretaker(originator);
	caretaker->save(originator->createMemento()); // 儲存初始狀态
	originator->showState(); // 列印初始狀态

	originator->changeState(string{ "狀态1" }); // 狀态發生改變1
	caretaker->save(originator->createMemento());
	originator->showState();

	originator->changeState(string{ "狀态2" }); // 狀态發生改變2
	caretaker->save(originator->createMemento());
	originator->showState();
	
	caretaker->undo(0); // 傳回到最初狀态
	originator->showState();

	DELETE(caretaker);
	DELETE(originator);
}
           

mian.cpp

#include <iostream>

extern void doMementoPattern();

int main()
{
	doMementoPattern();

	system("pause");
	return 1;
}
           
C++設計模式——備忘錄模式(memento pattern)一、原理講解二、實作代碼三、總結四、參考内容

三、總結

備忘錄模式核心在于儲存一個對象某個時刻部分或者全部狀态,以便客戶傳回時可以取消撤回那個時刻的狀态。也就是說,核心思想是儲存一個對象狀态,當狀态改變後用儲存的對象指派給目前對象即可。其實作跟原型模式不同,原型模式主要是對整個對象的克隆,無法做到部分克隆以及接口對所有客戶開放,破壞開閉原則。

3.1優點

  • 提供某種狀态(實際是對象)的回退和前進功能;
  • 實作了狀态的封裝,客戶不需要關注細節即可實作回退等功能;

3.2缺點

  • 如果儲存的狀态很多,也就是幾十個瞬間的狀态,當狀态對象記憶體很大時,将會非常耗記憶體;
  • 每儲存一次,就會發生一次對象深拷貝,占用不少CPU資源,需要仔細考慮是否需要這麼多備忘錄(備份);

四、參考内容

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

設計模式——備忘錄模式(C++實作)

設計模式C++實作(2)——備忘錄模式

陳建忠設計模式(參考:哔哩哔哩C++設計模式!!!)

Erich Gamma,Richard Helm.《設計模式 可複用面向對象軟體的基礎》[M].機械工業出版社,2019:

繼續閱讀