在响应某些事件时,应用程序需要保存自身的状态,比如当用户保存文档或者程序退出时。具体一点:当游戏退出之前,可能需要保存当前会话的状态,如游戏等级、敌人的数量、可用武器的种类等。当游戏再次打开时,玩家可以从离开的地方接着玩。很多时候,保存程序的状态真的不需要什么特别的方法,任何简单有效的方法都可以,但是保存的信息应该只对原始程序有意义。
模式定义
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保持这个状态,这样以后就可将该对象恢复到原先保持的状态。
简单来讲,备忘录模式就是一个对象的备份模式,提供了一种程序数据的备份方法,其类图如下:
类图中的三个角色:
1、Originator发起人角色
记录当前时刻的内部状态,负责定义哪些属于备份范围的状态,负责创建和恢复备忘录的数据。即负责创建一个 memento对象,用以记录当前时刻该对象的内部状态并可以使用备忘录恢复当前状态。
2、Memento备忘录角色
负责存储Originator发起人对象的内部状态,在需要的时候提供发起人需要的内部状态,并可以防止Originator以外的其它对象访问memento。
3、CareTaker备忘录管理员角色
对备忘录进行管理,保存和提供备忘录。
整体思路:Originator创建一个包含其状态的Memento,并交由CareTaker进行管理。
模式使用场景
1、无论什么时候你想存储和恢复对象的状态,都可以考虑使用备忘录模式。即需要保存和恢复数据的相关状态场景。
2、提供一个可回滚的操作,能够回到之前的状态。比如:ID浏览器中的后退按钮,文件管理器上的backspac等。
3、用于获取状态的接口会暴露实现接口,需要将其屏蔽起来。
iOS中的备忘录模式
在iOS中归档、属性列表序列化和核心数据都采用了备忘录模式。
模式优点
状态恢复机制,使得用户可以方便的回到一个特定的历史步骤,当新的状态无效或者存在问题时,可以使用存储起来的备忘录恢复状态。
模式缺点
资源消耗过大,特别是当保存对象的成员太多,那么久不可避免的需要占用大量的存储空间,每保存一次对象都需要消耗一定的资源。
模式-Demo
简单模拟一个游戏场景,定义一个游戏类
// MARK: - Originator
public class Game: Codable {
// MARK: - Memento
typealias GameMemento = Data
public class State: Codable {
public var attemptsRemaining: Int = 3
public var level: Int = 1 //等级
public var score: Int = 0 //分数
}
public var state = State()
public func rackUpMassivePionts() {
state.score += 9002
}
public func mostersEatPlayer() {
state.attemptsRemaining -= 1
}
}
定义一个游戏系统即CareTaker管理备忘存储
// MARK: - CareTaker
public class GameSystem {
private let decoder = JSONDecoder()
private let encoder = JSONEncoder()
private let userDefaults = UserDefaults.standard
// 封装存储逻辑
public func save(_ game: Game, title: String) throws {
let data = try encoder.encode(game)
userDefaults.set(data, forKey: title)
}
// 获取存储的对象
public func load(title: String) throws -> Game {
guard let data = userDefaults.data(forKey: title),
let game = try? decoder.decode(Game.self, from: data) else {
throw Error.gameNotFound
}
return game
}
public enum Error: String, Swift.Error {
case gameNotFound
}
}
简单使用
var game = Game()
game.mostersEatPlayer()
game.rackUpMassivePionts()
// Save Game
let gameSystem = GameSystem()
try? gameSystem.save(game, title: "Best Game Ever")
// Load Game
game = try! gameSystem.load(title: "Best Game Ever")
print("load game ever: \(game.state.score)")
参考
Dessign Pattern - Memento Pattern