1. 何為備忘錄模式
備忘錄模式(memento)是指在不破壞封裝性的前提下,捕獲一個(gè)對(duì)象的內(nèi)部狀態(tài),并在該對(duì)象之外保存這個(gè)狀態(tài),這樣以后就可以將該對(duì)象恢復(fù)到原先保存的狀態(tài)。
這個(gè)模式有三個(gè)關(guān)鍵角色:原發(fā)器(Originator)、備忘錄(Memento)和看管人(caretaker)。其思想非常簡(jiǎn)單,原發(fā)器創(chuàng)建一個(gè)包含其狀態(tài)的備忘錄,并傳給看管人。看管人不知道如何與備忘錄交互,但會(huì)把備忘錄放在安全之處保管好。它們的靜態(tài)關(guān)系如圖1-1所示:

當(dāng)看管人請(qǐng)求Originator對(duì)象保存其狀態(tài)時(shí),Originator對(duì)象將使用其內(nèi)部狀態(tài)創(chuàng)建一個(gè)新的Memento實(shí)例。然后看管人保管Memento實(shí)例,或者把它保存到文件系統(tǒng),一段時(shí)間后再把它傳回給Originator對(duì)象。Originator對(duì)象不知道這個(gè)Memento對(duì)象將如何被保存??垂苋艘膊恢繫emento對(duì)象里是什么。圖1-2中的時(shí)序圖解釋了他們之間的交互過程。

這個(gè)設(shè)計(jì)的關(guān)鍵是維持Memento對(duì)象的私有性,只讓Originator對(duì)象訪問保存在Memento對(duì)象的內(nèi)部狀態(tài)(即Originator過去的內(nèi)部狀態(tài))。Memento類應(yīng)該有兩個(gè)接口:一個(gè)寬接口,給Originator用;一個(gè)窄接口,給其它對(duì)象用。上面圖中的setState:、 state和init方法應(yīng)該定義為私有,不讓Originator和Memento以外的對(duì)象使用。
2. 應(yīng)用場(chǎng)景及代碼實(shí)現(xiàn)
一般來說滿足一下兩個(gè)條件時(shí),應(yīng)當(dāng)考慮使用這一模式:
- 需要保存一個(gè)對(duì)象(或部分)在某一個(gè)時(shí)刻的狀態(tài),這樣以后就可以恢復(fù)到先前的狀態(tài);
- 用于獲取狀態(tài)的接口會(huì)暴露實(shí)現(xiàn)的細(xì)節(jié),需要將其隱藏起來。
備忘錄模式在棋類游戲(悔棋)、普通軟件(撤銷操作)、數(shù)據(jù)庫軟件(事物管理的回滾操作)和Photoshop軟件中體現(xiàn)較多。這里以游戲存檔為例:
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("當(dāng)前游戲的模式:\(game.pattern)---游戲的進(jìn)度:\(game.progress)")
let caretaker: CareTaker = CareTaker()
caretaker.memento = game.saveMemento()
game.progress = 0.5
print("當(dāng)前游戲的模式:\(game.pattern)---游戲的進(jìn)度:\(game.progress)")
print("備份游戲的模式:\(caretaker.memento!.pattern)---游戲的進(jìn)度:\(caretaker.memento!.progress)")
運(yùn)行結(jié)果:
當(dāng)前游戲的模式:1V1---游戲的進(jìn)度:0.2
當(dāng)前游戲的模式:1V1---游戲的進(jìn)度:0.5
備份游戲的模式:1V1---游戲的進(jìn)度:0.2
3. 優(yōu)缺點(diǎn)
-
優(yōu)點(diǎn):
- 狀態(tài)恢復(fù)機(jī)制,使得用戶可以方便地回到一個(gè)特定的歷史步驟,當(dāng)新的狀態(tài)無效或者存在問題時(shí),可以使用暫時(shí)存儲(chǔ)起來的備忘錄將狀態(tài)復(fù)原.
- 信息的封裝,一個(gè)備忘錄對(duì)象是一種原發(fā)器對(duì)象狀態(tài)的表示,不會(huì)被其他代碼所改動(dòng)。備忘錄保存了原發(fā)器的狀態(tài),采用列表、堆棧等集合來存儲(chǔ)備忘錄對(duì)象可以實(shí)現(xiàn)多次撤銷操作.
-
缺點(diǎn):
- 資源消耗過大,如果需要保存的原發(fā)器類的成員變量太多,就不可避免需要占用大量的存儲(chǔ)空間,每保存一次對(duì)象的狀態(tài)都需要消耗一定的系統(tǒng)資源.