持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第21天,点击查看活动详情
本文主要介绍iOS设计模式中的备忘录模式。备忘录顾名思义就是通过记录对象的状态,以便后面可以恢复保持到原先的状态。
1. 什么是备忘录模式
备忘录定义为“作为某人或某事的提醒物或纪念品而保留下来的物品
”。类似我的即时贴
,我们有的时候有想法了或者想要提醒一些事的时候写在即时贴上,后面这个信息用过了就可以扔到垃圾桶。
我们借用类似的思想,来保存对象的状态
并在后来进行恢复
。状态本身被创建为一种对象形式
。它封装了原始对象的内部状态
。只有创建即时贴的原始对象
才能看懂保存的状态
并用它恢复
原来的状态。
在响应某些事件时,应用程序需要保存自身的状态
,比如当用户保存文档或程序退出时。比如,退出游戏之前,可能需要保存当前会话的状态,比如游戏等级,敌人数量,可用武器等。再次打开的时候,玩家就可以从离开的地方接着玩,或者我们编辑文档的时候,我么可以通过撤销
回到之前的状态,这些程序需要保存当前上下文的复杂状态的快照并在以后恢复。
备忘录模式:在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样以后就可以改对象恢复到原先保存的状态。
2. 什么时候时候使用备忘录模式
满足下面的两个条件时,应当考虑使用这一模式:
- 需要保存一个对象(或某部分)在某一个
时刻的状态
,这样以后就可以恢复到先前的状态 - 用于获取状态的接口会
暴漏实现
的细节,需要将其隐藏
起来。
3. 代码展示
备忘录模式主要有3个关键角色:原发器(Originator),备忘录(Memento),和看管人(Caretaker)
。原发器创建一个包含其状态的备忘录,并传给看管人。看管人不知如何与备忘录交互
,但会把备忘录放在安全之处保管好。当看管人请求原发器对象
保存其状态时,原发器对象将使用内部状态创建一个新的备忘录实例
。然后看管人保存备忘录对象。
import XCTest
/// The Originator holds some important state that may change over time. It also
/// defines a method for saving the state inside a memento and another method
/// for restoring the state from it.
class Originator {
/// For the sake of simplicity, the originator's state is stored inside a
/// single variable.
private var state: String
init(state: String) {
self.state = state
print("Originator: My initial state is: (state)")
}
/// The Originator's business logic may affect its internal state.
/// Therefore, the client should backup the state before launching methods
/// of the business logic via the save() method.
func doSomething() {
print("Originator: I'm doing something important.")
state = generateRandomString()
print("Originator: and my state has changed to: (state)")
}
private func generateRandomString() -> String {
return String(UUID().uuidString.suffix(4))
}
/// Saves the current state inside a memento.
func save() -> Memento {
return ConcreteMemento(state: state)
}
/// Restores the Originator's state from a memento object.
func restore(memento: Memento) {
guard let memento = memento as? ConcreteMemento else { return }
self.state = memento.state
print("Originator: My state has changed to: (state)")
}
}
/// The Memento interface provides a way to retrieve the memento's metadata,
/// such as creation date or name. However, it doesn't expose the Originator's
/// state.
protocol Memento {
var name: String { get }
var date: Date { get }
}
/// The Concrete Memento contains the infrastructure for storing the
/// Originator's state.
class ConcreteMemento: Memento {
/// The Originator uses this method when restoring its state.
private(set) var state: String
private(set) var date: Date
init(state: String) {
self.state = state
self.date = Date()
}
/// The rest of the methods are used by the Caretaker to display metadata.
var name: String { return state + " " + date.description.suffix(14).prefix(8) }
}
/// The Caretaker doesn't depend on the Concrete Memento class. Therefore, it
/// doesn't have access to the originator's state, stored inside the memento. It
/// works with all mementos via the base Memento interface.
class Caretaker {
private lazy var mementos = [Memento]()
private var originator: Originator
init(originator: Originator) {
self.originator = originator
}
func backup() {
print("\nCaretaker: Saving Originator's state...\n")
mementos.append(originator.save())
}
func undo() {
guard !mementos.isEmpty else { return }
let removedMemento = mementos.removeLast()
print("Caretaker: Restoring state to: " + removedMemento.name)
originator.restore(memento: removedMemento)
}
func showHistory() {
print("Caretaker: Here's the list of mementos:\n")
mementos.forEach({ print($0.name) })
}
}
/// Let's see how it all works together.
class MementoConceptual: XCTestCase {
func testMementoConceptual() {
let originator = Originator(state: "Super-duper-super-puper-super.")
let caretaker = Caretaker(originator: originator)
caretaker.backup()
originator.doSomething()
caretaker.backup()
originator.doSomething()
caretaker.backup()
originator.doSomething()
print("\n")
caretaker.showHistory()
print("\nClient: Now, let's rollback!\n\n")
caretaker.undo()
print("\nClient: Once more!\n\n")
caretaker.undo()
}
}
执行结果
Originator: My initial state is: Super-duper-super-puper-super.
Caretaker: Saving Originator's state...
Originator: I'm doing something important.
Originator: and my state has changed to: 1923
Caretaker: Saving Originator's state...
Originator: I'm doing something important.
Originator: and my state has changed to: 74FB
Caretaker: Saving Originator's state...
Originator: I'm doing something important.
Originator: and my state has changed to: 3681
Caretaker: Here's the list of mementos:
Super-duper-super-puper-super. 11:45:44
1923 11:45:44
74FB 11:45:44
Client: Now, let's rollback!
Caretaker: Restoring state to: 74FB 11:45:44
Originator: My state has changed to: 74FB
Client: Once more!
Caretaker: Restoring state to: 1923 11:45:44
Originator: My state has changed to: 1923
4. 小结
我们开发的过程中比如对象的归档和解档
,属性列表序列化
和核心数据
中采用了备忘录模式。比如我们实现绘画的涂鸦
或者文档编辑
器经常会有撤销
的操作,这就需要我们保存之前的操作状态使用备忘录模式。备忘录模式主要是状态的保存
以及隐藏获取状态
实现的细节。