三十、备忘录模式

1. 何为备忘录模式

备忘录模式(memento)是指在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样以后就可以将该对象恢复到原先保存的状态。

这个模式有三个关键角色:原发器(Originator)、备忘录(Memento)和看管人(caretaker)。其思想非常简单,原发器创建一个包含其状态的备忘录,并传给看管人。看管人不知道如何与备忘录交互,但会把备忘录放在安全之处保管好。它们的静态关系如图1-1所示:

三十、备忘录模式_第1张图片
图1-1 备忘录模式结构的类图

当看管人请求Originator对象保存其状态时,Originator对象将使用其内部状态创建一个新的Memento实例。然后看管人保管Memento实例,或者把它保存到文件系统,一段时间后再把它传回给Originator对象。Originator对象不知道这个Memento对象将如何被保存。看管人也不知道Memento对象里是什么。图1-2中的时序图解释了他们之间的交互过程。

三十、备忘录模式_第2张图片
图1-2 备忘录模式的时序图

这个设计的关键是维持Memento对象的私有性,只让Originator对象访问保存在Memento对象的内部状态(即Originator过去的内部状态)。Memento类应该有两个接口:一个宽接口,给Originator用;一个窄接口,给其它对象用。上面图中的setState:、 state和init方法应该定义为私有,不让Originator和Memento以外的对象使用。

2. 应用场景及代码实现

一般来说满足一下两个条件时,应当考虑使用这一模式:

  • 需要保存一个对象(或部分)在某一个时刻的状态,这样以后就可以恢复到先前的状态;
  • 用于获取状态的接口会暴露实现的细节,需要将其隐藏起来。

备忘录模式在棋类游戏(悔棋)、普通软件(撤销操作)、数据库软件(事物管理的回滚操作)和Photoshop软件中体现较多。这里以游戏存档为例:

import Foundation

class Game {
    var time: Float = 0
    var pattern: String = ""
    var progress: Float = 0
    
    func saveMemento() -> Memento {
        return Memento(pattern: self.pattern, progress: self.progress)
    }
}

class Memento {
    var pattern: String = ""
    var progress: Float = 0
    
    convenience init(pattern: String, progress: Float) {
        self.init()
        self.pattern = pattern
        self.progress = progress
    }
}

class CareTaker {
    var memento: Memento?
}

let game: Game = Game()
game.time = 2500
game.pattern = "1V1"
game.progress = 0.2

print("当前游戏的模式:\(game.pattern)---游戏的进度:\(game.progress)")

let caretaker: CareTaker = CareTaker()
caretaker.memento = game.saveMemento()

game.progress = 0.5

print("当前游戏的模式:\(game.pattern)---游戏的进度:\(game.progress)")

print("备份游戏的模式:\(caretaker.memento!.pattern)---游戏的进度:\(caretaker.memento!.progress)")

运行结果:

当前游戏的模式:1V1---游戏的进度:0.2
当前游戏的模式:1V1---游戏的进度:0.5
备份游戏的模式:1V1---游戏的进度:0.2

3. 优缺点

  • 优点:

    • 状态恢复机制,使得用户可以方便地回到一个特定的历史步骤,当新的状态无效或者存在问题时,可以使用暂时存储起来的备忘录将状态复原.
    • 信息的封装,一个备忘录对象是一种原发器对象状态的表示,不会被其他代码所改动。备忘录保存了原发器的状态,采用列表、堆栈等集合来存储备忘录对象可以实现多次撤销操作.
  • 缺点:

    • 资源消耗过大,如果需要保存的原发器类的成员变量太多,就不可避免需要占用大量的存储空间,每保存一次对象的状态都需要消耗一定的系统资源.

你可能感兴趣的:(三十、备忘录模式)