備忘錄模式。顧名思義,備忘錄模式的初衷就是為了返回上一個狀態而設計的。從名字看起來一目瞭然,好吧,還是老樣子,先給出定義。
備忘錄(Memento):在不破壞封裝性的前提下,捕獲一個對象的內部狀態,並在該對象之外儲存這個狀態。這樣以後就可將該對象恢複到原先儲存的狀態。
定義看起來搞的很專業,其實就是儲存上一個狀態,以便日後恢複用。好比是在玩遊戲,在打大Boss之前擔心第一次打不過,先存個盤,萬一玩兒完了,還可以恢複狀態重新PK。
下面給出類結構圖。
Originator(原發器):記錄當前時刻的內部狀態,負責定義哪些屬於需要備份的狀態,負責建立memento,負責從memento恢複狀態。
Memento(備忘錄):負責儲存Originator的內部狀態,在需要時提供給Originator內部狀態。
Caretaker(看管人):將Memento儲存在安全的地方,並負責提取。
一句話概括:Originator建立一個包含其狀態的Memento交給Caretaker保管,Caretaker不知如何與Memento互動,只負責把Memento在安全的地方儲存好。
從上面這張圖來看,關係比較簡單吧。那麼備忘錄模式一般都用在什麼場合呢?
Memento模式比較適用於功能比較複雜的,但需要維護或記錄屬性曆史的類,或者需要儲存的屬性只是眾多屬性中的一小部分時,Originator可以根據儲存的Memento資訊還原到前一狀態。有時候一些對象的內部資訊必須儲存在對象以外的地方,但是必須要由對象自己讀取,這時,使用備忘錄可以把複雜的對象內部資訊對其他的對象屏蔽起來。當然了,最大的作用還是在於當角色的狀態改變的時候,有可能這個狀態無效,這時候就可以使用暫時儲存起來的備忘錄將狀態進行複原。好啦,其實翻來覆去就是為了恢複資料用的,車軲轆話就不多說了,下面給大家簡單展示一下實現的代碼吧。
Objective-C代碼實現:
Originator:
複製代碼 代碼如下:
//發起人:記錄當前時刻的內部狀態,負責定義哪些屬於備份範圍的狀態,負責建立和恢複備忘錄資料。
#import <Foundation/Foundation.h>
@class NimoMemento;
@interface NimoOriginator : NSObject
@property (nonatomic, copy) NSString* state;
- (NimoMemento *)createMemento;
- (void)restoreMemento:(NimoMemento *)memento;
@end
複製代碼 代碼如下:
#import "NimoOriginator.h"
#import "NimoMemento.h"
@implementation NimoOriginator
- (NimoMemento *)createMemento
{
NimoMemento *memento = [[NimoMemento alloc] initWithState:_state];
return memento;
}
- (void)restoreMemento:(NimoMemento *)memento
{
_state = memento.state;
}
- (NSString *)description
{
return [NSString stringWithFormat:@"State:%@", _state];
}
@end
Memento:
複製代碼 代碼如下:
//備忘錄:負責儲存發起人對象的內部狀態,在需要的時候提供發起人需要的內部狀態。
#import <Foundation/Foundation.h>
@interface NimoMemento : NSObject
@property (nonatomic, copy, readonly) NSString *state;
- (id)initWithState:(NSString *)state;
@end
複製代碼 代碼如下:
#import "NimoMemento.h"
@interface NimoMemento()
@property (nonatomic, copy, readwrite) NSString *state;
@end
複製代碼 代碼如下:
@implementation NimoMemento
- (id)initWithState:(NSString *)state
{
if (self = [super init]) {
_state = [state copy];
}
return self;
}
@end
Caretaker:
複製代碼 代碼如下:
//管理角色:對備忘錄進行管理,儲存和提供備忘錄。
#import <Foundation/Foundation.h>
@class NimoMemento;
@interface NimoCaretaker : NSObject
@property (nonatomic, assign) NimoMemento *memento;
@end
複製代碼 代碼如下:
//
// NimoCaretaker.m
// MementoDemo
//
#import "NimoCaretaker.h"
@implementation NimoCaretaker
@end
複製代碼 代碼如下:
Client:
#import <Foundation/Foundation.h>
#import "NimoOriginator.h"
#import "NimoMemento.h"
#import "NimoCaretaker.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
NimoOriginator *originator = [[NimoOriginator alloc] init];
originator.state = @"Old";
NSLog(@"%@", originator);
NimoMemento *memento = originator.createMemento;
NimoCaretaker *caretaker = [[NimoCaretaker alloc] init];
caretaker.memento = memento;
originator.state = @"New";
NSLog(@"%@", originator);
[originator restoreMemento:[caretaker memento]];
NSLog(@"%@", originator);
}
return 0;
}
運行:
2015-08-12 20:27:39.184 MementoDemo[1160:34914] State:Old2015-08-12 20:27:39.186 MementoDemo[1160:34914] State:New2015-08-12 20:27:39.186 MementoDemo[1160:34914] State:Old
以上通用代碼運行後雖然能得到期望的結果,但是並不完美,在Menmento類的實現中,我們把state屬性以及initWithState初始化方法暴露在了公用介面中,這兩者本應只提供給Originator與Menmento(即對Originator與Menmento提供寬介面,對Caretaker等其他對象提供窄介面)。在C++等其他物件導向語言中,一般使用private或friend進行聲明。但在Objective-C中一切都是公有的,所以需要額外的技巧來實現。
通過類擴充將state屬性以及initWithState初始化方法從主介面標頭檔NimoMemento.h中分離:
複製代碼 代碼如下:
//
// NimoMemento+Private.h
// MementoDemo
//
#import "NimoMemento.h"
@interface NimoMemento ()
@property (nonatomic, copy, readwrite) NSString *state;
- (id)initWithState:(NSString *)state;
@end
如此,只在Originator與Menmento中#import NimoMemento+Private.h,便實現了介面的私人化。