1. 备忘录模式概述
备忘录模式提供一种状态恢复的实现机制, 用户可以方便的回到一个特定的历史步骤, 当新的状态无效或者存在问题时候, 可以使用暂时存起来的备忘录将状态复原, 当前很多软件中提供的撤销操作, 其中使用了备忘录模式.
备忘录模式: 在不破坏封装的前提下, 捕获一个对象的内部状态, 并在该对象之外保存这个状态, 这样可以在以后将状态恢复到原先保存的状态. 它是一种对象行为模式.
备忘录模式的核心是备忘录类以及用于管理备忘录的负责人类的设计, 其结构如下所示:
- 原发器(Originator): 它是一个普通类, 可以创建一个备忘录, 并存储它的当前内部状态, 也可以使用备忘录来恢复其内部状态, 一般将需要保存内部状态的类设计为原发器.
- 备忘录(Memento): 存储原发器的内部状态, 根据原发器来决定保存那些内部状态. 备忘录的设计一般可以参考原发器的设计, 根据实际需要确定备忘录中的属性. 需要注意的是除了原发器本身和负责人之外, 备忘录对象不能直接提供其它类使用, 原发器的设计在不同的编程语言中实现机制会有所不同.
- 负责人(Caretaker): 负责人又称为管理者, 它负责保存备忘录, 但是不能对备忘录的内容进行检查和操作. 在负责人类中有一个或者多个备忘录对象, 它只负责存储对象, 而不能修改对象, 也无需知道对象的实现细节.
备忘录模式关键在于如何设计备忘录类和负责人类. 由于在备忘录中存储的是原发器的中间状态, 因此需要防止原发器以外的其它对象访问备忘录, 特别是不允许其它对象来修改备忘录.
2. 中国象棋例子的Swift实现
备忘录模式, Swift版本客户端代码:
备忘录模式代码:
// ChessMan
class ChessMan: NSObject {
var label: String?
var x: Int?
var y: Int?
// 初始化棋子状态
init(label: String, x: Int, y: Int) {
self.label = label
self.x = x
self.y = y
}
// 移动棋子
func move(x: Int, y: Int) {
self.x = x
self.y = y
}
// 保存状态, 返回当前对象状态的类, 即备忘录
func save() -> ChessmanMemento {
return ChessmanMemento(chessman: self)
}
// 恢复到一个历史状态, 参数是一个状态
func restore(memento: ChessmanMemento) {
label = memento.label
x = memento.x
y = memento.y
}
}
// ChessmanMemento
class ChessmanMemento: NSObject {
var label: String
var x: Int
var y: Int
init(chessman: ChessMan) {
label = chessman.label ?? ""
x = chessman.x ?? 0
y = chessman.y ?? 0
}
}
// MementoCaretaker
class MementoCaretaker: NSObject {
lazy var mementoArray = [ChessmanMemento]()
// 根据历史状态, 从状态列表中获取一个历史状态
func getMemento(index: Int) -> ChessmanMemento {
return mementoArray.remove(at: index)
}
// 获取最近的状态
func getLatestMemento() -> ChessmanMemento {
return mementoArray.removeLast()
}
// 获取最老的的状态
func getOldestMemento() -> ChessmanMemento {
return mementoArray.removeFirst()
}
// 添加一个状态
func addMemento(memento: ChessmanMemento) {
mementoArray.append(memento)
}
}
3. 备忘录模式的封装
备忘录是一个很特殊的对象,只有原发器对它拥有控制的权力,负责人只负责管理,而其他类无法访问到备忘录,因此我们需要对备忘录进行封装。
为了实现对备忘录对象的封装,需要对备忘录的调用进行控制,对于原发器而言,它可以调用备忘录的所有信息,允许原发器访问返回到先前状态所需的所有数据;对于负责人而言,只负责备忘录的保存并将备忘录传递给其他对象;对于其他对象而言,只需要从负责人处取出备忘录对象并将原发器对象的状态恢复,而无须关心备忘录的保存细节。理想的情况是只允许生成该备忘录的那个原发器访问备忘录的内部状态。
在实际开发中,原发器与备忘录之间的关系是非常特殊的,它们要分享信息而不让其他类知道,实现的方法因编程语言的不同而有所差异,在C++中可以使用friend关键字,让原发器类和备忘录类成为友元类,互相之间可以访问对象的一些私有的属性;在Java语言中可以将原发器类和备忘录类放在一个包中,让它们之间满足默认的包内可见性,也可以将备忘录类作为原发器类的内部类,使得只有原发器才可以访问备忘录中的数据,其他对象都无法使用备忘录中的数据。
4. 备忘录模式总结
备忘录模式在很多软件的使用过程中普遍存在,但是在应用软件开发中,它的使用频率并不太高,因为现在很多基于窗体和浏览器的应用软件并没有提供撤销操作。如果需要为软件提供撤销功能,备忘录模式无疑是一种很好的解决方案。在一些字处理软件、图像编辑软件、数据库管理系统等软件中备忘录模式都得到了很好的应用。
4.1 主要优点
- 它提供了一种状态恢复的实现机制,使得用户可以方便地回到一个特定的历史步骤,当新的状态无效或者存在问题时,可以使用暂时存储起来的备忘录将状态复原。
- 备忘录实现了对信息的封装,一个备忘录对象是一种原发器对象状态的表示,不会被其他代码所改动。备忘录保存了原发器的状态,采用列表、堆栈等集合来存储备忘录对象可以实现多次撤销操作。
4.2 主要缺点
资源消耗过大,如果需要保存的原发器类的成员变量太多,就不可避免需要占用大量的存储空间,每保存一次对象的状态都需要消耗一定的系统资源。
4.3 适用场景
- 保存一个对象在某一个时刻的全部状态或部分状态,这样以后需要时它能够恢复到先前的状态,实现撤销操作。
- 防止外界对象破坏一个对象历史状态的封装性,避免将对象历史状态的实现细节暴露给外界对象。
Reference: http://blog.csdn.net/lovelion/article/details/7526747