行为型模式之备忘录模式

备忘录模式(Memento Pattern)保存一个对象的某个状态,以便在适当的时候恢复对象.备忘录模式属于行为型模式

这个模式的思想对大家来说应该很熟悉,很多时候在编程的时候,不小心删了某一段代码或者写错了,咱们都用CMD+Z恢复到原来的状态,那么怎么做到的呢?就是使用备忘录模式,在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态。

其实很好理解,当你不知道你接下来要做的事情是对是错的时候,那么先把当前做的保存下来,即使下面的你做错了,也可以回到现在的状态,备忘录的目的就是为了允许用户取消不确定或者错误的操作,能够恢复到他原先的状态,使得他有"后悔药"可吃。

模式结构和说明

在这里插入图片描述

发起人:记录当前时刻的内部状态,负责定义哪些属于备份范围的状态,负责创建和恢复备忘录数据。
备忘录: 负责存储发起人对象的内部状态,在需要的时候提供发起人需要的内部状态
管理角色:备忘录管理者,或者称为备忘录负责人。主要负责保存备忘录对象,但是不能对备忘录对象的内容进行操作或检查。

示例代码

  1. 创建协议
    创建协议的原因是因为备忘录存储的数据不一定是什么类型,我们要让备忘录能存储很多类型的数据,需要让需要备忘的类实现一套统一的接口.
    协议包含两个方法,一个获取当前状态的方法,和一个恢复状态的方法.需要使用备忘录的类在这两个方法实现存储和恢复的逻辑
    有的人会问,写个基类让被人继承不可以吗?可以,但耦合性会很强
@protocol  MementoCenterProtocol <NSObject>

/**
 获取状态
 */
- (id)getStatus;

/**
 恢复状态
 */
- (void)recoverFromStatus:(id)status;

@end
  1. 创建备忘录类
    备忘录类负责数据的存储和恢复.传过来的object必须是遵循上面协议的对象,这个就可以调用协议里的方法获得需要备忘的数据了
@implementation MementoCenter

+ (void)saveMementoObject:(id<MementoCenterProtocol>)object withKey:(NSString *)key
{
    NSParameterAssert(object);
    NSParameterAssert(key);
    id data = [object getStatus];
    NSData *tempData = [FastCoder dataWithRootObject:data];
    if (tempData) {
        [[NSUserDefaults standardUserDefaults] setObject:tempData forKey:key];
    }
}

+ (id)mementoObjectWithKey:(NSString *)key
{
    NSParameterAssert(key);
    id data = nil;
    NSData *tempData = [[NSUserDefaults standardUserDefaults] objectForKey:key];
    if (tempData) {
        data = [FastCoder objectWithData:tempData];
    }
    return data;
}

@end

这里需要注意一下,二进制序列化的时候引入了FastCoder,可以普通对象直接转换成NSData,直接存储,效率高于NSCoding,编码也比NSCoding好用

  1. 实践(先定义一个NSObject的类,再客户端应用)
@implementation People

- (id)getStatus
{
    return @{@"name":self.name,
             @"age":@(self.age)
             };
}

- (void)recoverFromStatus:(id)status
{
    NSDictionary *dic = status;
    self.name = dic[@"name"];
    self.age = [dic[@"age"] integerValue];
}
@end
- (void)viewDidLoad {
    [super viewDidLoad];
    People *people = [[People alloc] init];
    people.name = @"张三";
    people.age = 18;
    
    [MementoCenter saveMementoObject:people withKey:@"key"];
    people.name = @"李四";
    id p = [MementoCenter mementoObjectWithKey:@"key"];
    [people recoverFromStatus:p];
    NSLog(@"name:%@--age:%ld",people.name,(long)people.age);
}
  1. 结果
    在这里插入图片描述
    大家可以看出,上面代码修改过people的name值,但恢复数据后,值并没有改变

大家仔细研究一下可以看出,上面的代码有个问题,有人会想很完美啊,完全实现了想要的功能.没错,功能实现没有任何问题,但是你把你实现的细节都暴露出去了,面向对象讲究的是面向接口编程,内部暴露越少越好,何况使用起来也很不方便,如果可以直接使用People对象保存,恢复那多方便啊,那就增加一个NSObject的分类


@implementation NSObject (MementoCenter)

- (void)saveMementoWithKey:(NSString *)key
{
    id<MementoCenterProtocol> obj = (id<MementoCenterProtocol>)self;
    if ([obj respondsToSelector:@selector(getStatus)]) {
        [MementoCenter saveMementoObject:obj withKey:key];
    }
}

- (void)recoverStatusWithKey:(NSString *)key
{
    id status = [MementoCenter mementoObjectWithKey:key];
    id <MementoCenterProtocol> obj = (id<MementoCenterProtocol>)self;
    if ([obj respondsToSelector:@selector(recoverFromStatus:)]) {
        [obj recoverFromStatus:status];
    }
}

@end

客户端使用:

   People *people = [[People alloc] init];
    people.name = @"张三";
    people.age = 18;
    [people saveMementoWithKey:@"key"];
    people.name = @"李四";
    [people recoverStatusWithKey:@"key"];
    

现在使用起来是不是很方便了,而且我不用管是谁去保存,恢复的

模式讲解

模式的功能

备忘录的功能,首先是在不破坏封装性的前提下,捕获一个对象的内部状态.这里注意两点,一是不破坏封装性,也就是对象不能暴露它不应该暴露的细节,另外一个是捕获的是对象的内部状态,而且通常运行期间某个时刻对象的内部状态

为什么要捕获这个对象的内部状态呢?捕获这个内部状态有什么用呢?
是要在以后的某个时候,将该对象的状态恢复到备忘录所保存的状态,这才是备忘录真正的目的,前面保存状态就是为了后面恢复,虽然不是一定要恢复,但是目的是为了恢复。这也是很多人理解备忘录模式的时候,忽视掉的地方,他们太关注备忘,而忽视了恢复,这是不全面的理解。

捕获的状态存放在哪里呢?
备忘录模式中,捕获的内部状态,存储在备忘录对象中,而备忘录对象,通常会被存储在原发器对象之外,也就是被保存状态的对象的外部,通常是存放在管理者对象那里

原发器对象

原发器对象,就是需要被保存状态的对象,也是有可能需要恢复状态的对象,原发器一般会包含备忘录对象的实现
通常原发器对象应该提供捕获某个时刻对象内部状态的方法,在这个方法里面,原发器对象会创建备忘录对象,把需要保存的状态数据设置到备忘录对象中,然后把备忘录对象提供给管理者对象来保存。
当然,原发器对象也应该提供这样的方法:按照外部要求来恢复内部状态到某个备忘录对象记录的状态。

Demo地址

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值