(-) 提出问题
我们在编程的时候,经常需要保存对象的一个状态,当需要的时候,可以恢复到这个状态。比如,我们使用Xcode进行编程时,假如编写失误(例如不小心误删除了几行代码),我们希望返回删除前的状态,便可以使用Ctrl+Z来进行返回。这时我们便可以使用备忘录模式来实现。
(二 )备忘录设计模式 简介
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样以后就可以将对象恢复到原先保存的的状态。——《设计模式》
通用类图如下:
使用场景:
当满足以下两个条件的时候,应当考虑使用这一模式。
1、需要保存一个对象(活某部分)在某一时刻的状态,这样以后就可以恢复先前的状态;
2、用于获取状态的接口,会暴露实现的细节,需要将其隐藏起来;
(二 )使用备忘录设计模式
通过一个简单的例子来实现这个设计模式。例子很简单,只是完成一个类的一个属性保存和恢复。
第1步 创建协议
创建协议的原因是因为备忘录存储的数据不一定是什么类型的,我们要让备忘录能存储很多类型的数据,需要让需要备忘的类实现一套统一的接口。
协议包含两个方法,一个获取当前状态的方法,和一个恢复状态的方法。需要使用备忘录的类在这两个方法里实现存储和恢复的逻辑。
#import <Foundation/Foundation.h>
@protocol MementoCenterProtocol <NSObject>
/*
获得状态
*/
-(id)getStatus;
/*
设置状态
*/
-(void)setStatus:(id)data;
@end
第2步 创建备忘录类
备忘录中心类只负责数据的存储和恢复。传过来的object必须是继承上面协议的对象,这样就可以调用协议里的方法获得需要备忘的数据了。类里有两个方法。
根据key保存状态
/**
* 保存备忘录
*
* @param object object
* @param key key
*/
+(void)saveMementoObject:(id<MementoCenterProtocol>)object withKey:(NSString*)key{
if (object==nil || key==nil) {
return;
}
id data = [object getStatus];
NSData *tmpData = [FastCoder dataWithRootObject:data];
// 进行存储
if (tmpData) {
[[NSUserDefaults standardUserDefaults] setObject:tmpData
forKey:key];
}
}
根据key获得object状态数据
/**
* 获得保存的状态数据
*
* @param key key
*
* @return 状态
*/
+(id)getMementObjectWithKey:(NSString*)key{
if (key==nil) {
return nil;
}
id data = nil;
NSData *tmpData = [[NSUserDefaults standardUserDefaults] objectForKey:key];
if (tmpData) {
data = [FastCoder objectWithData:tmpData];
}
return data;
}
这两个方法里面用到了一个类FastCoder的方法,FastCoder是一个Cocoa object和object graph的高性能二进制序列化格式,可以作为Property Lists和JSON的替代选择。FastCoder git地址。
第3步 创建工具类
如果我们直接调用备忘录的方法的话,会暴露出我们实现的细节,也会暴露出我们要备忘的object。所以我们创建一个工具类,来再封装一层。这样我们就不会暴露具体的实现细节了。
/**
* 备忘状态
*
* @param key 键值
*/
- (void)saveStateWithKey:(NSString *)key;
/**
* 恢复状态
*
* @param key 键值
*/
- (void)recoverFromStateWithKey:(NSString *)key;
实现:
- (void)saveStateWithKey:(NSString *)key{
if (key==nil) {
return;
}
id<MementoCenterProtocol> obj = (id<MementoCenterProtocol>)self;
if ([obj respondsToSelector:@selector(getStatus)]) {
[MementoCenter saveMementoObject:obj withKey:key];
}
}
- (void)recoverFromStateWithKey:(NSString *)key{
if (key==nil) {
return;
}
id state = [MementoCenter getMementObjectWithKey:key];
id<MementoCenterProtocol> obj = (id<MementoCenterProtocol>)self;
if ([obj respondsToSelector:@selector(setStatus:)]) {
[obj setStatus:state];
}
}
第4步 创建测试类
测试类里只有一个字符串属性,我们来恢复这个属性。测试类要实现上面协议的方法。
-(id)getStatus{
return self.valueStr.length? self.valueStr: @"当前运动:10.00KM";
}
-(void)setStatus:(id)data{
if (data) {
self.valueStr = (NSString*)data;
}
}
第5步 测试
TestView *texeView = [[TestView alloc]initWithFrame:CGRectMake(100, 100, 100, 100)];
[self.view addSubview:texeView];
texeView.valueStr = @"idage";
NSLog(@"恢复前 ===%@",texeView.valueStr );
//保存
[texeView saveStateWithKey:@"test"];
texeView.valueStr = @"idage163";
//恢复
[texeView recoverFromStateWithKey:@"test"];
NSLog(@"恢复后 ===%@",texeView.valueStr );
这样我们就是实现了既能保存和恢复状态,也不会暴露保存和恢复的细节。
(四)鸣谢
这篇文章是看过 YouXianMing老师 的教程后,得到的启发并重构自己项目代码后写的总结。希望对大家有点帮助。在此感谢YouXianMing老师。