設計模式-10 - Memento Design Pattern?
?
1.定義
備忘錄模式是一種設計模式,它允許在不破壞封裝性的情況下捕獲和恢復對象的內部狀態。
其結構:
Originator:創建和管理備忘錄的對象。
Memento:存儲 Originator 狀態的備忘錄對象。Memento 對象是不可變的。
Caretaker:存儲 Memento 對象,而不了解其實際內容。
示例:
一個文本編輯器可以使用備忘錄模式來存儲文檔的狀態,以便用戶可以撤消和重做編輯操作。Originator 是文檔對象,Memento 是存儲文檔狀態的文本快照,Caretaker 是管理備忘錄的撤消/重做管理器。
? ? ? ? +--------------+| Originator ? |+--------------+|v+--------------+| Memento ? ? ?|+--------------+|v+--------------+| Caretaker ? ?|+--------------+|v(stores Memento)
調用關系:
- Originator 創建一個 Memento 對象來存儲其當前狀態。
- Caretaker 從 Originator 接收 Memento 對象并將其存儲起來。
- Originator 修改其狀態。
- Originator 從 Caretaker 恢復其狀態,傳遞先前創建的 Memento 對象。
- Caretaker 將 Memento 對象傳遞給 Originator。
- Originator 使用 Memento 對象恢復其狀態。
說明:
Originator:創建和修改其狀態,并創建 Memento 對象來存儲其狀態。
Memento:存儲 Originator 的狀態,以便以后可以恢復。
Caretaker:負責存儲和管理 Memento 對象。
?? ??? ??? ?
2.內涵
工作原理:
Originator 創建一個 Memento 對象,該對象捕獲其當前狀態。
Originator 可以將 Memento 對象傳遞給 Caretaker。
當 Originator 需要恢復其狀態時,它可以從 Caretaker 請求 Memento 對象。
Originator 使用 Memento 對象恢復其內部狀態。
3.使用示例
#include <iostream>
#include <string>
#include <vector>// Originator: The object whose state needs to be saved and restored.
class Originator {
private:std::string state;public:void SetState(const std::string& newState) {state = newState;}std::string GetState() const {return state;}// Memento: Inner class representing the state of the Originator.class Memento {private:std::string state;public:Memento(const std::string& originatorState) : state(originatorState) {}std::string GetSavedState() const {return state;}};// Create a Memento object to save the current state.Memento CreateMemento() const {return Memento(state);}// Restore the state from a Memento object.void RestoreState(const Memento& memento) {state = memento.GetSavedState();}
};// Caretaker: Manages the Memento objects.
class Caretaker {
private:std::vector<Originator::Memento> mementos;public:void AddMemento(const Originator::Memento& memento) {mementos.push_back(memento);}Originator::Memento GetMemento(int index) const {if (index >= 0 && index < mementos.size()) {return mementos[index];}throw std::out_of_range("Invalid Memento index");}
};int main() {Originator originator;Caretaker caretaker;originator.SetState("State 1");caretaker.AddMemento(originator.CreateMemento());originator.SetState("State 2");caretaker.AddMemento(originator.CreateMemento());// Restore to the previous stateoriginator.RestoreState(caretaker.GetMemento(0));std::cout << "Current state: " << originator.GetState() << std::endl;// Restore to an even earlier stateoriginator.RestoreState(caretaker.GetMemento(1));std::cout << "Current state: " << originator.GetState() << std::endl;return 0;
}
4.注意事項
備忘錄模式需要注意的事項:
- 只存儲相關狀態:Memento 對象應僅存儲 Originator 的相關狀態,而不是其所有狀態。這有助于保持 Memento 的大小較小并提高性能。
- 避免存儲引用:Memento 對象不應存儲對其他對象的引用,因為這可能會導致循環引用和內存泄漏。
- 考慮序列化:如果需要在進程或計算機之間傳輸 Memento 對象,則需要考慮序列化和反序列化機制。
- 版本控制:如果 Originator 的狀態可能會隨著時間的推移而改變,則需要考慮使用版本控制機制來管理 Memento 對象的不同版本。
- 并發訪問:在并發環境中使用備忘錄模式時,需要確保 Originator、Memento 和 Caretaker 類都是線程安全的,并且并發訪問備忘錄是原子的。
- 性能影響:頻繁創建和存儲 Memento 對象可能會對應用程序的性能產生影響,尤其是在處理大型或復雜的狀態時。
- 設計復雜性:備忘錄模式可能會增加應用程序的設計復雜性,尤其是在需要管理多個 Originator 和 Memento 對象時
5.最佳實踐
當需要以下場景時,需要使用備忘錄設計模式:
- 當需要在不破壞封裝性的情況下捕獲和恢復對象的內部狀態時。
- 當需要多次撤消和重做操作時。
- 當需要將復雜的對象圖存儲為快照時。
6.總結
在設計一個需要頻繁保存狀態的應用,如何確保備忘錄模式在并發環境中有效應對并發訪問,這里有些方法可以借鑒:
- 線程安全:確保 Originator、Memento 和 Caretaker 類都是線程安全的。這可以采用多種方法實現,例如使用同步機制(例如鎖或互斥量)或使用不可變對象。
- 只讀備忘錄:使 Memento 對象不可變。這將防止并發訪問時意外修改備忘錄的內容。
- 并發控制:在 Caretaker 中使用適當的并發控制機制,例如讀寫鎖或原子操作,以確保對備忘錄的訪問是原子的。
- 隔離備忘錄:為每個 Originator 實例維護一個單獨的備忘錄存儲。這將防止不同 Originator 實例之間的并發訪問沖突。
- 使用版本控制:為備忘錄引入版本控制機制。這將允許跟蹤和恢復備忘錄的不同版本,即使在并發訪問的情況下也是如此。
其他考慮因素:
- 性能:在高并發環境中,確保備忘錄模式的實現不會引入明顯的性能開銷。
- 可擴展性:備忘錄模式的實現應該具有可擴展性,以便在需要時可以輕松擴展以支持更多的并發訪問。
- 測試:徹底測試備忘錄模式的實現,以確保其在并發環境中按預期工作。
通過遵循這些準則,你可以確保備忘錄模式在需要頻繁保存狀態的并發應用程序中能有效應對并發訪問。