iOS 設(shè)計(jì)模式之十九(備忘錄模式)

一靠粪、概念

1蜡吧、備忘錄模式的動(dòng)機(jī)

? "曾經(jīng)有一段真摯的愛情放在我面前毫蚓,我沒有珍惜,等到失去了才后悔莫及昔善,如果上天再給我一次機(jī)會(huì)元潘,我會(huì)對(duì)她說三個(gè)字,我愛你耀鸦,如果非得加一個(gè)期限柬批,我希望是一萬(wàn)年。"有沒有很熟悉這句臺(tái)詞袖订,這句話出自周星馳主演的電影《大話西游之月光寶盒》。人生沒有“后悔藥”嗅虏,但是我們可以實(shí)現(xiàn)軟件中的“后悔藥”洛姑,這就需要了解備忘錄模式了。

2皮服、備忘錄模式的定義

? 備忘錄模式(Memento Pattern):在不破壞封裝的前提下楞艾,捕獲一個(gè)對(duì)象的內(nèi)部狀態(tài),并在該對(duì)象之外保存這個(gè)狀態(tài)龄广,這樣可以在以后將對(duì)象恢復(fù)到原先保存的狀態(tài)硫眯。它是一種對(duì)象行為型模式,其別名為Token择同。

3两入、備忘錄模式的3個(gè)角色

1)Originator(原發(fā)器):它是一個(gè)普通類,可以創(chuàng)建一個(gè)備忘錄敲才,并存儲(chǔ)它的當(dāng)前內(nèi)部狀態(tài)裹纳,也可以使用備忘錄來恢復(fù)其內(nèi)部狀態(tài),一般將需要保存內(nèi)部狀態(tài)的類設(shè)計(jì)為原發(fā)器紧武。

2)Memento(備忘錄):存儲(chǔ)原發(fā)器的內(nèi)部狀態(tài)剃氧,根據(jù)原發(fā)器來決定保存哪些內(nèi)部狀態(tài)。備忘錄的設(shè)計(jì)一般可以參考原發(fā)器的設(shè)計(jì)阻星,根據(jù)實(shí)際需要確定備忘錄類中的屬性朋鞍。需要注意的是,除了原發(fā)器本身與負(fù)責(zé)人類之外妥箕,備忘錄對(duì)象不能直接供其他類使用滥酥,原發(fā)器的設(shè)計(jì)在不同的編程語(yǔ)言中實(shí)現(xiàn)機(jī)制會(huì)有所不同。

3)Caretaker(負(fù)責(zé)人):負(fù)責(zé)人又稱為管理者矾踱,它負(fù)責(zé)保存?zhèn)渫浐薇罚遣荒軐?duì)備忘錄的內(nèi)容進(jìn)行操作或檢查。在負(fù)責(zé)人類中可以存儲(chǔ)一個(gè)或多個(gè)備忘錄對(duì)象呛讲,它只負(fù)責(zé)存儲(chǔ)對(duì)象禾怠,而不能修改對(duì)象返奉,也無(wú)須知道對(duì)象的實(shí)現(xiàn)細(xì)節(jié)。

4吗氏、結(jié)構(gòu)圖
備忘錄模式

二芽偏、示例

? 備忘錄模式的核心是備忘錄類以及用于管理備忘錄的負(fù)責(zé)人類的設(shè)計(jì)。

1)先創(chuàng)建一個(gè)象棋棋子類Chessman弦讽,類中有一些屬性污尉、保存狀態(tài)和恢復(fù)狀態(tài)的方法,表示原發(fā)器往产;

2)然后創(chuàng)建棋子備忘錄ChessmanMemento類被碗,表示備忘錄;

3)最后創(chuàng)建MementoCaretaker類仿村,類中有添加備忘錄和獲取指定的備忘錄的方法锐朴,表示負(fù)責(zé)人;

具體代碼如下:

Chessman類:

// 象棋棋子類:原發(fā)器
@interface Chessman : NSObject
@property(nonatomic, copy) NSString *name;
@property(nonatomic, assign) NSInteger x;
@property(nonatomic, assign) NSInteger y;
- (instancetype)initWithName:(NSString *)name x:(NSInteger)x y:(NSInteger)y;
- (ChessmanMemento *)save; //保存狀態(tài)
- (void)restore:(ChessmanMemento *)memento; //恢復(fù)狀態(tài)
@end

@implementation Chessman
- (instancetype)initWithName:(NSString *)name x:(NSInteger)x y:(NSInteger)y {
    self = [super init];
    if (self) {
        _name = name;
        _x = x;
        _y = y;
    }
    return self;
}

- (ChessmanMemento *)save {
    return [[ChessmanMemento alloc] initWithName:self.name x:self.x y:self.y];
}

- (void)restore:(ChessmanMemento *)memento {
    self.name = memento.name;
    self.x = memento.x;
    self.y = memento.y;
}
@end

ChessmanMemento類:

// 象棋棋子備忘錄類
@interface ChessmanMemento : NSObject
@property(nonatomic, copy) NSString *name;
@property(nonatomic, assign) NSInteger x;
@property(nonatomic, assign) NSInteger y;
- (instancetype)initWithName:(NSString *)name x:(NSInteger)x y:(NSInteger)y;
@end

@implementation ChessmanMemento
- (instancetype)initWithName:(NSString *)name x:(NSInteger)x y:(NSInteger)y {
    self = [super init];
    if (self) {
        _name = name;
        _x = x;
        _y = y;
    }
    return self;
}
@end

MementoCaretaker類:

// 象棋棋子備忘錄管理類
@interface MementoCaretaker : NSObject
- (void)addMemento:(ChessmanMemento *)memento;
- (ChessmanMemento *)getMemento:(NSInteger)index;
@end

@interface MementoCaretaker ()
@property(nonatomic, strong) NSMutableArray *mementoList;
@end
@implementation MementoCaretaker
- (instancetype)init
{
    self = [super init];
    if (self) {
        _mementoList = [NSMutableArray array];
    }
    return self;
}

- (void)addMemento:(ChessmanMemento *)memento {
    [self.mementoList addObject:memento];
}

- (ChessmanMemento *)getMemento:(NSInteger)index {
    if (index < self.mementoList.count) {
        return self.mementoList[index];
    }
    return nil;
}
@end

運(yùn)行代碼:

@interface ViewController ()
@property(nonatomic, assign) NSInteger index; //備忘錄索引
@property(nonatomic, strong) MementoCaretaker *caretaker; //負(fù)責(zé)人
@end

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.index = -1;
    self.caretaker = [MementoCaretaker new];
    
    Chessman *chess = [[Chessman alloc] initWithName:@"車" x:1 y:1];
    [self playWithChess:chess];
    
    chess.y = 8;
    [self playWithChess:chess];
    
    chess.x = 6;
    [self playWithChess:chess];
    
    NSLog(@"------------------");
    [self undoWithChess:chess];
    [self undoWithChess:chess];
}

// 下棋
- (void)playWithChess:(Chessman *)chess {
    ChessmanMemento *memento = [chess save];
    [self.caretaker addMemento:memento]; //保存?zhèn)渫?    self.index ++; //序號(hào)加1
    NSLog(@"下棋啦蔼囊,%@當(dāng)前位置:x = %ld, y = %ld", chess.name, chess.x, chess.y);
}

// 悔棋
- (void)undoWithChess:(Chessman *)chess {
    self.index --; //序號(hào)減1
    ChessmanMemento *memento = [self.caretaker getMemento:self.index];
    [chess restore:memento]; //恢復(fù)備忘錄
    NSLog(@"悔棋啦焚志,%@當(dāng)前位置:x = %ld, y = %ld", chess.name, chess.x, chess.y);
}
@end

打印結(jié)果:

下棋啦,車當(dāng)前位置:x = 1, y = 1
下棋啦畏鼓,車當(dāng)前位置:x = 1, y = 8
下棋啦酱酬,車當(dāng)前位置:x = 6, y = 8
------------------
悔棋啦,車當(dāng)前位置:x = 1, y = 8
悔棋啦云矫,車當(dāng)前位置:x = 1, y = 1

三膳沽、總結(jié)

? 備忘錄模式在很多軟件的使用過程中普遍存在,如果需要為軟件提供撤銷功能泼差,備忘錄模式無(wú)疑是一種很好的解決方案贵少。

1、優(yōu)點(diǎn)

1堆缘、它提供了一種狀態(tài)恢復(fù)的實(shí)現(xiàn)機(jī)制滔灶,使得用戶可以方便地回到一個(gè)特定的歷史步驟,當(dāng)新的狀態(tài)無(wú)效或者存在問題時(shí)吼肥,可以使用暫時(shí)存儲(chǔ)起來的備忘錄將狀態(tài)復(fù)原录平。

2、備忘錄實(shí)現(xiàn)了對(duì)信息的封裝缀皱,一個(gè)備忘錄對(duì)象是一種原發(fā)器對(duì)象狀態(tài)的表示斗这,不會(huì)被其他代碼所改動(dòng)。備忘錄保存了原發(fā)器的狀態(tài)啤斗,采用列表表箭、堆棧等集合來存儲(chǔ)備忘錄對(duì)象可以實(shí)現(xiàn)多次撤銷操作。

2钮莲、缺點(diǎn)

? 如果需要保存的原發(fā)器類的成員變量太多免钻,就不可避免需要占用大量的存儲(chǔ)空間彼水,每保存一次對(duì)象的狀態(tài)都需要消耗一定的系統(tǒng)資源。

3极舔、適用場(chǎng)景

1凤覆、保存一個(gè)對(duì)象在某一個(gè)時(shí)刻的全部狀態(tài)或部分狀態(tài),這樣以后需要時(shí)它能夠恢復(fù)到先前的狀態(tài)拆魏,實(shí)現(xiàn)撤銷操作盯桦。

2、防止外界對(duì)象破壞一個(gè)對(duì)象歷史狀態(tài)的封裝性渤刃,避免將對(duì)象歷史狀態(tài)的實(shí)現(xiàn)細(xì)節(jié)暴露給外界對(duì)象拥峦。

4、iOS應(yīng)用舉例

? Cocoa Touch框架在歸檔解檔卖子、屬性列表序列化和CoreData中采用了備忘錄模式事镣。

Demo地址:iOS-Design-Patterns

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市揪胃,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌氛琢,老刑警劉巖喊递,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異阳似,居然都是意外死亡骚勘,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門撮奏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來俏讹,“玉大人,你說我怎么就攤上這事畜吊≡蠼” “怎么了?”我有些...
    開封第一講書人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵玲献,是天一觀的道長(zhǎng)殉疼。 經(jīng)常有香客問我,道長(zhǎng)捌年,這世上最難降的妖魔是什么瓢娜? 我笑而不...
    開封第一講書人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮礼预,結(jié)果婚禮上眠砾,老公的妹妹穿的比我還像新娘。我一直安慰自己托酸,他們只是感情好褒颈,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開白布柒巫。 她就那樣靜靜地躺著,像睡著了一般哈肖。 火紅的嫁衣襯著肌膚如雪吻育。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,692評(píng)論 1 305
  • 那天淤井,我揣著相機(jī)與錄音布疼,去河邊找鬼。 笑死币狠,一個(gè)胖子當(dāng)著我的面吹牛游两,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播漩绵,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼贱案,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了止吐?” 一聲冷哼從身側(cè)響起宝踪,我...
    開封第一講書人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎碍扔,沒想到半個(gè)月后瘩燥,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡不同,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年厉膀,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片二拐。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡服鹅,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出百新,到底是詐尸還是另有隱情企软,我是刑警寧澤,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布吟孙,位于F島的核電站澜倦,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏杰妓。R本人自食惡果不足惜藻治,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望巷挥。 院中可真熱鬧桩卵,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至钩乍,卻和暖如春辞州,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背寥粹。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工变过, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人涝涤。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓媚狰,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親阔拳。 傳聞我的和親對(duì)象是個(gè)殘疾皇子崭孤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 1.初識(shí)備忘錄模式 在不破壞封裝性的前提下,捕獲一個(gè)對(duì)象的內(nèi)部狀態(tài)糊肠,并在該對(duì)象之外保存這個(gè)狀態(tài)辨宠。這樣以后就可將該對(duì)...
    王偵閱讀 477評(píng)論 0 0
  • 在閻宏博士的《JAVA與模式》一書中開頭是這樣描述備忘錄(Memento)模式的:備忘錄模式又叫做快照模式(Sna...
    Ant_way閱讀 788評(píng)論 0 0
  • 目錄 本文的結(jié)構(gòu)如下: 引言 什么是備忘錄模式 模式的結(jié)構(gòu) 典型代碼 代碼示例 優(yōu)點(diǎn)和缺點(diǎn) 適用環(huán)境 模式應(yīng)用 一...
    w1992wishes閱讀 596評(píng)論 0 1
  • 備忘錄模式(行為型) 原書鏈接設(shè)計(jì)模式(劉偉) 一、相關(guān)概述 備忘錄模式提供了一種狀態(tài)恢復(fù)的實(shí)現(xiàn)機(jī)制货裹,使得用戶可以...
    哈哈大圣閱讀 352評(píng)論 0 0
  • 《兒童鋼琴教學(xué)論》泪酱,共十章,總的來說比較適合教師對(duì)教學(xué)進(jìn)行分析研究还最,當(dāng)然琴童家長(zhǎng)也可以看一看墓阀。其中除了一些常見的理...
    chrispiano閱讀 822評(píng)論 1 3