1. 何為備忘錄模式
備忘錄模式(memento)是指在不破壞封裝性的前提下,捕獲一個對象的內部狀態,并在該對象之外保存這個狀態,這樣以后就可以將該對象恢復到原先保存的狀態。
這個模式有三個關鍵角色:原發器(Originator)、備忘錄(Memento)和看管人(caretaker)。其思想非常簡單,原發器創建一個包含其狀態的備忘錄,并傳給看管人。看管人不知道如何與備忘錄交互,但會把備忘錄放在安全之處保管好。它們的靜態關系如圖1-1所示:
圖1-1 備忘錄模式結構的類圖
當看管人請求Originator對象保存其狀態時,Originator對象將使用其內部狀態創建一個新的Memento實例。然后看管人保管Memento實例,或者把它保存到文件系統,一段時間后再把它傳回給Originator對象。Originator對象不知道這個Memento對象將如何被保存。看管人也不知道Memento對象里是什么。圖1-2中的時序圖解釋了他們之間的交互過程。
圖1-2 備忘錄模式的時序圖
這個設計的關鍵是維持Memento對象的私有性,只讓Originator對象訪問保存在Memento對象的內部狀態(即Originator過去的內部狀態)。Memento類應該有兩個接口:一個寬接口,給Originator用;一個窄接口,給其它對象用。上面圖中的setState:、 state和init方法應該定義為私有,不讓Originator和Memento以外的對象使用。
2. 應用場景及代碼實現
一般來說滿足一下兩個條件時,應當考慮使用這一模式:
- 需要保存一個對象(或部分)在某一個時刻的狀態,這樣以后就可以恢復到先前的狀態;
- 用于獲取狀態的接口會暴露實現的細節,需要將其隱藏起來。
備忘錄模式在棋類游戲(悔棋)、普通軟件(撤銷操作)、數據庫軟件(事物管理的回滾操作)和Photoshop軟件中體現較多。這里以游戲存檔為例:
import Foundation
class Game {
var time: Float = 0
var pattern: String = ""
var progress: Float = 0
func saveMemento() -> Memento {
return Memento(pattern: self.pattern, progress: self.progress)
}
}
class Memento {
var pattern: String = ""
var progress: Float = 0
convenience init(pattern: String, progress: Float) {
self.init()
self.pattern = pattern
self.progress = progress
}
}
class CareTaker {
var memento: Memento?
}
let game: Game = Game()
game.time = 2500
game.pattern = "1V1"
game.progress = 0.2
print("當前游戲的模式:\(game.pattern)---游戲的進度:\(game.progress)")
let caretaker: CareTaker = CareTaker()
caretaker.memento = game.saveMemento()
game.progress = 0.5
print("當前游戲的模式:\(game.pattern)---游戲的進度:\(game.progress)")
print("備份游戲的模式:\(caretaker.memento!.pattern)---游戲的進度:\(caretaker.memento!.progress)")
運行結果:
當前游戲的模式:1V1---游戲的進度:0.2
當前游戲的模式:1V1---游戲的進度:0.5
備份游戲的模式:1V1---游戲的進度:0.2
3. 優缺點
-
優點:
- 狀態恢復機制,使得用戶可以方便地回到一個特定的歷史步驟,當新的狀態無效或者存在問題時,可以使用暫時存儲起來的備忘錄將狀態復原.
- 信息的封裝,一個備忘錄對象是一種原發器對象狀態的表示,不會被其他代碼所改動。備忘錄保存了原發器的狀態,采用列表、堆棧等集合來存儲備忘錄對象可以實現多次撤銷操作.
-
缺點:
- 資源消耗過大,如果需要保存的原發器類的成員變量太多,就不可避免需要占用大量的存儲空間,每保存一次對象的狀態都需要消耗一定的系統資源.