長沙網(wǎng)站策劃在線優(yōu)化網(wǎng)站
設(shè)計(jì)模式20-備忘錄
- 動機(jī)
- 定義與結(jié)構(gòu)
- 定義
- 結(jié)構(gòu)
- C++代碼推導(dǎo)
- 優(yōu)缺點(diǎn)
- 應(yīng)用場景
- 總結(jié)
- 備忘錄模式和序列化
- 備忘錄模式
- 1. **動機(jī)**
- 2. **實(shí)現(xiàn)方式**
- 3. **應(yīng)用場景**
- 4. **優(yōu)點(diǎn)**
- 5. **缺點(diǎn)**
- 序列化
- 1. **動機(jī)**
- 2. **實(shí)現(xiàn)方式**
- 3. **應(yīng)用場景**
- 4. **優(yōu)點(diǎn)**
- 5. **缺點(diǎn)**
- 對比總結(jié)
動機(jī)
- 在軟件構(gòu)建過程中,某些對象的狀態(tài)在轉(zhuǎn)換過程中可能由于某種需要這個程序能夠回溯到對象之前處于某個點(diǎn)時的狀態(tài)。使用一些公用接口來讓其他對象得到對象的狀態(tài),便會暴露對象細(xì)節(jié)的實(shí)現(xiàn)
- 如何實(shí)現(xiàn)對象狀態(tài)的良好保存與恢復(fù)?但同時又不會因此而破壞對象本身的封裝性呢。
- 在軟件開發(fā)中,有時需要在不破壞對象封裝性的前提下捕獲并存儲對象的內(nèi)部狀態(tài),以便稍后可以將對象恢復(fù)到以前的狀態(tài)。這種需求通常出現(xiàn)在實(shí)現(xiàn)撤銷/恢復(fù)操作的場景中,例如文本編輯器中的撤銷操作。直接暴露對象的內(nèi)部狀態(tài)會違反封裝原則,而備忘錄模式提供了一種解決方案,使得狀態(tài)恢復(fù)操作能夠在不破壞封裝性的前提下實(shí)現(xiàn)。
定義與結(jié)構(gòu)
定義
備忘錄模式在不破壞封裝性的前提下,捕獲對象的內(nèi)部狀態(tài),并在該對象之外保存這個狀態(tài)。這樣以后就可以將對象恢復(fù)到原先保存的狀態(tài)。
結(jié)構(gòu)
解釋這張圖:
這張圖描繪了一個典型的UML(統(tǒng)一建模語言)結(jié)構(gòu)圖,展示了“備忘錄模式”(Memento Pattern)的核心組件及其關(guān)系。備忘錄模式是一種用于捕獲和存儲一個對象內(nèi)部狀態(tài)的方式,以便可以在未來某個時刻將對象恢復(fù)到這個狀態(tài)。這個模式特別適用于需要保存和恢復(fù)對象狀態(tài)的場景,比如撤銷操作、事務(wù)處理等。
圖中包含了三個主要的類:
-
Originator(原發(fā)器):這個類是需要被保存狀態(tài)的對象。它包含一個
CreateMemento()
方法,用于創(chuàng)建一個備忘錄對象(Memento),這個對象包含了Originator的當(dāng)前狀態(tài)。Originator還包含一個SetMemento(Memento m)
方法,用于設(shè)置Originator的狀態(tài)為備忘錄對象(Memento)中存儲的狀態(tài)。圖中對SetMemento
方法的描述可能有些誤解,因?yàn)橥ǔ_@個方法不會直接從外部傳入一個備忘錄對象來設(shè)置狀態(tài),而是由Caretaker或者系統(tǒng)其他部分將之前保存的備忘錄對象傳遞回Originator來恢復(fù)狀態(tài)。 -
Memento(備忘錄):這個類用于存儲Originator的內(nèi)部狀態(tài),以保護(hù)這些狀態(tài)不受外部訪問。它包含了一個構(gòu)造函數(shù)(盡管圖中沒有直接標(biāo)出),該構(gòu)造函數(shù)可能接受來自O(shè)riginator的當(dāng)前狀態(tài)作為參數(shù)。Memento還包含
GetState()
和SetState(state)
方法(盡管圖中SetState
方法的參數(shù)可能標(biāo)記有誤,通常備忘錄模式中的Memento類不需要SetState
方法,因?yàn)閭渫浿饕怯脕泶鎯顟B(tài)的)。但在實(shí)際實(shí)現(xiàn)中,GetState()
方法用于返回備忘錄中存儲的狀態(tài),而SetState
(如果存在的話)可能并不是Memento類的一部分,而是用于在Originator中恢復(fù)狀態(tài)的過程中的一部分,但這通常是通過將Memento對象傳遞給Originator的某個恢復(fù)狀態(tài)方法來實(shí)現(xiàn)的。 -
Caretaker(管理者):這個類負(fù)責(zé)保存?zhèn)渫泴ο?#xff0c;但不檢查備忘錄對象的內(nèi)容。在圖中,Caretaker類被描述為有一個
ReturnNewMemento(state)
方法,這實(shí)際上可能是一個誤解或錯誤。在標(biāo)準(zhǔn)的備忘錄模式中,Caretaker類會維護(hù)一個備忘錄對象的列表,但不直接創(chuàng)建新的Memento對象。相反,它可能包含一個AddMemento(Memento m)
方法來添加新的備忘錄對象,以及一個GetMemento(index)
或類似的方法來檢索之前保存的備忘錄對象。圖中的ReturnNewMemento(state)
可能試圖表達(dá)Caretaker返回一個新狀態(tài)的Memento對象給Originator,但這并不是Caretaker類的典型職責(zé);更常見的是,Caretaker簡單地存儲和檢索Memento對象。
圖中箭頭表示類之間的交互關(guān)系。從Originator到Memento的箭頭表示Originator創(chuàng)建Memento對象的過程,而從Caretaker到Originator的箭頭(盡管圖中沒有直接畫出)表示Caretaker可能將存儲的Memento對象傳遞回Originator以恢復(fù)狀態(tài)。
需要注意的是,圖中的某些細(xì)節(jié)(如ReturnNewMemento(state)
方法和SetState0
方法的標(biāo)記)可能與標(biāo)準(zhǔn)的備忘錄模式實(shí)現(xiàn)不完全一致,可能是為了簡化圖示或特定實(shí)現(xiàn)的表示。
C++代碼推導(dǎo)
以下是一個簡單的備忘錄模式的C++實(shí)現(xiàn)示例,模擬一個文本編輯器的撤銷功能。
備忘錄類:
#include <iostream>
#include <string>// 備忘錄類,用于存儲Originator的內(nèi)部狀態(tài)
class Memento {
private:std::string state;public:Memento(const std::string& state) : state(state) {}std::string getState() const {return state;}
};
發(fā)起人類:
class Originator {
private:std::string state;public:void setState(const std::string& state) {this->state = state;std::cout << "State set to: " << state << std::endl;}std::string getState() const {return state;}Memento* createMemento() {return new Memento(state);}void setMemento(Memento* memento) {state = memento->getState();std::cout << "State restored to: " << state << std::endl;}
};
負(fù)責(zé)人類:
class Caretaker {
private:Memento* memento;public:void saveMemento(Memento* memento) {this->memento = memento;}Memento* getMemento() {return memento;}~Caretaker() {delete memento;}
};
客戶端代碼:
int main() {Originator* originator = new Originator();Caretaker* caretaker = new Caretaker();originator->setState("State1");caretaker->saveMemento(originator->createMemento());originator->setState("State2");caretaker->saveMemento(originator->createMemento());originator->setState("State3");// 恢復(fù)到上一個狀態(tài)originator->setMemento(caretaker->getMemento());delete originator;delete caretaker;return 0;
}
運(yùn)行結(jié)果:
State set to: State1
State set to: State2
State set to: State3
State restored to: State2
優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
- 封裝性:備忘錄模式可以在不破壞對象封裝性的前提下保存和恢復(fù)對象的狀態(tài),符合對象的封裝原則。
- 簡化撤銷操作:備忘錄模式簡化了復(fù)雜系統(tǒng)中的撤銷操作實(shí)現(xiàn),使得對象能夠恢復(fù)到先前的狀態(tài)。
缺點(diǎn):
- 內(nèi)存開銷:如果對象的狀態(tài)較大且備忘錄創(chuàng)建頻繁,可能會占用大量內(nèi)存,增加系統(tǒng)開銷。
- 管理復(fù)雜性:備忘錄需要被妥善管理,尤其是在需要多個備忘錄進(jìn)行多步撤銷時,備忘錄的管理和恢復(fù)可能會變得復(fù)雜。
應(yīng)用場景
備忘錄模式在以下場景中應(yīng)用較多:
- 需要保存對象狀態(tài)以便恢復(fù):例如實(shí)現(xiàn)多步撤銷/恢復(fù)功能的場景,如文本編輯器、圖形編輯器等。
- 需要避免暴露對象內(nèi)部狀態(tài):當(dāng)需要避免外部直接訪問對象的內(nèi)部狀態(tài)時,可以使用備忘錄模式來實(shí)現(xiàn)狀態(tài)保存和恢復(fù)。
- 對象狀態(tài)變化復(fù)雜且不可預(yù)測:在對象狀態(tài)變化頻繁且不可預(yù)測的情況下,備忘錄模式可以有效管理這些狀態(tài)變化。
總結(jié)
- 備忘錄模式是一種強(qiáng)大的設(shè)計(jì)模式,通過封裝對象的內(nèi)部狀態(tài),實(shí)現(xiàn)了狀態(tài)的保存和恢復(fù)功能。雖然它能夠有效地支持撤銷和恢復(fù)操作,但在實(shí)際應(yīng)用中需要注意內(nèi)存消耗和管理復(fù)雜性,特別是在對象狀態(tài)復(fù)雜且變化頻繁的場景下。
- 備忘錄模式存儲原發(fā)器對象的內(nèi)部狀態(tài)。在需要時恢復(fù)原發(fā)器狀態(tài)。
- 備忘錄模式的核心是信息隱藏,即原發(fā)器需要向外界隱藏信息保持其封裝性,但同時又需要將再保存到外界。
- 由于現(xiàn)代語言運(yùn)行時如JAVA ,c#,都具有相當(dāng)?shù)膶ο笮蛄谢С帧R虼送捎眯瘦^高,又較容易正確實(shí)現(xiàn)的序列化方案來實(shí)現(xiàn)備忘錄模式。
備忘錄模式和序列化
備忘錄模式和序列化在某些方面具有相似的功能,但它們的目標(biāo)和應(yīng)用場景不同。
備忘錄模式
1. 動機(jī)
備忘錄模式的主要動機(jī)是保存和恢復(fù)對象的內(nèi)部狀態(tài),而不破壞對象的封裝性。它常用于實(shí)現(xiàn)撤銷/恢復(fù)操作,讓對象能夠恢復(fù)到之前的某個狀態(tài)。
2. 實(shí)現(xiàn)方式
備忘錄模式通過創(chuàng)建一個“備忘錄”對象,將發(fā)起人(Originator)對象的內(nèi)部狀態(tài)存儲在該備忘錄對象中。發(fā)起人可以使用這個備忘錄對象來恢復(fù)其內(nèi)部狀態(tài)。備忘錄模式通常包括三個角色:發(fā)起人、備忘錄和負(fù)責(zé)人(Caretaker),其中負(fù)責(zé)人僅負(fù)責(zé)管理備忘錄的保存和恢復(fù),不直接訪問備忘錄的內(nèi)容。
3. 應(yīng)用場景
- 撤銷/恢復(fù)操作:如文本編輯器、圖形編輯器中的撤銷功能。
- 歷史記錄管理:需要保存對象的狀態(tài)以便以后恢復(fù)。
- 避免對象內(nèi)部狀態(tài)暴露:需要保存和恢復(fù)對象狀態(tài)時,但不希望暴露對象的內(nèi)部實(shí)現(xiàn)。
4. 優(yōu)點(diǎn)
- 封裝性:備忘錄模式可以在不破壞對象封裝性的前提下保存和恢復(fù)狀態(tài)。
- 簡單性:對外界來說,狀態(tài)的保存和恢復(fù)過程是透明的,簡化了撤銷/恢復(fù)功能的實(shí)現(xiàn)。
5. 缺點(diǎn)
- 內(nèi)存消耗:如果對象的狀態(tài)較大或備忘錄頻繁創(chuàng)建,可能會導(dǎo)致較大的內(nèi)存開銷。
- 管理復(fù)雜性:當(dāng)涉及多個狀態(tài)備忘錄時,備忘錄的管理可能會變得復(fù)雜。
序列化
1. 動機(jī)
序列化的主要目的是將對象的狀態(tài)轉(zhuǎn)換為字節(jié)流,以便可以將其存儲在文件、數(shù)據(jù)庫或通過網(wǎng)絡(luò)傳輸,并在以后將其反序列化為原始對象。
2. 實(shí)現(xiàn)方式
序列化將對象的狀態(tài)轉(zhuǎn)換為字節(jié)流,并將其保存到存儲介質(zhì)中。反序列化則是將字節(jié)流重新轉(zhuǎn)換為對象。序列化通常依賴于語言或平臺提供的內(nèi)置機(jī)制,如Java的Serializable
接口或C++的自定義序列化邏輯。
3. 應(yīng)用場景
- 持久化存儲:將對象狀態(tài)保存到文件或數(shù)據(jù)庫中,以便以后恢復(fù)。
- 網(wǎng)絡(luò)傳輸:在分布式系統(tǒng)中,將對象狀態(tài)通過網(wǎng)絡(luò)傳輸?shù)搅硪粋€系統(tǒng)。
- 跨平臺數(shù)據(jù)交換:不同平臺之間的數(shù)據(jù)交換,需要將對象狀態(tài)轉(zhuǎn)換為通用的字節(jié)流格式。
4. 優(yōu)點(diǎn)
- 持久性:序列化使得對象狀態(tài)可以長期保存,并在需要時恢復(fù)。
- 跨平臺:序列化后的數(shù)據(jù)可以在不同平臺或系統(tǒng)之間傳輸和共享。
5. 缺點(diǎn)
- 性能開銷:序列化和反序列化可能會引入性能開銷,特別是在大型對象或頻繁操作的情況下。
- 安全性:序列化數(shù)據(jù)可能會暴露對象的內(nèi)部狀態(tài),存在安全風(fēng)險,如果反序列化的對象來自不可信的來源,可能會導(dǎo)致安全漏洞。
對比總結(jié)
-
用途不同:
- 備忘錄模式:主要用于保存和恢復(fù)對象的狀態(tài),以支持撤銷/恢復(fù)操作,強(qiáng)調(diào)對象的封裝性。
- 序列化:主要用于持久化或跨網(wǎng)絡(luò)傳輸對象的狀態(tài),強(qiáng)調(diào)對象的持久性和可傳輸性。
-
實(shí)現(xiàn)方式不同:
- 備忘錄模式:通常是在內(nèi)存中保存對象的狀態(tài),并通過備忘錄對象管理狀態(tài)的保存和恢復(fù)。
- 序列化:將對象的狀態(tài)轉(zhuǎn)換為字節(jié)流,存儲在外部介質(zhì)中或通過網(wǎng)絡(luò)傳輸。
-
封裝性:
- 備忘錄模式:注重保持對象的封裝性,外部系統(tǒng)無法訪問對象的內(nèi)部狀態(tài)。
- 序列化:通常會暴露對象的內(nèi)部狀態(tài),可能會引發(fā)安全問題。
-
應(yīng)用場景不同:
- 備忘錄模式:適用于需要頻繁保存和恢復(fù)對象狀態(tài)的場景,如撤銷功能。
- 序列化:適用于對象的持久化存儲、網(wǎng)絡(luò)傳輸、跨平臺數(shù)據(jù)交換等場景。
備忘錄模式更適合在應(yīng)用程序內(nèi)部管理對象的狀態(tài)轉(zhuǎn)換,而序列化更適合在應(yīng)用程序與外部系統(tǒng)之間交換或持久化數(shù)據(jù)。兩者可以結(jié)合使用,例如在分布式系統(tǒng)中,備忘錄模式管理對象狀態(tài),而序列化用于將狀態(tài)持久化或傳輸?shù)狡渌?jié)點(diǎn)。