8.0 备忘录模式(Memento Pattern)

一.定义

每个人都会犯错,都希望有"后悔药"能弥补自己过失,在计算机应用中,客户同样会常常犯错,希望回退到某个历史状态,如:Eclipse中使用Ctrl+Z 回退键,该需求可使用备忘录模式实现,该模式能记录一个对象的内部状态,当用户后悔时能撤销当前操作,使数据恢复到它原先的某个历史状态;

备忘录模式(Memento Pattern)

1) 在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以后就可通过该对象恢复到原先保存的状态;

2) 又称快照模式,属于行为模式,是用来记录一个对象的某种状态或某些数据,当要回退时,可从备忘录对象中获取原数据进行恢复;

3) 简单说:一个对象中一般都封装了很多属性,这些属性的值会随程序的运行而变化,当我们需要保存某一个时刻该对象的某些值时,

    我们就再创建一个对象,将当前对象的一些属性保存到新的对象中,当我们需要恢复的时候再从新的对象中取出属性值即可;

二. 特点

1. 优点

1) 给用户提供了一种可以恢复状态的机制,当用户需要时能比较方便地将数据恢复到某个历史状态;

2) 实现了内部状态的封装,除了创建它的发起者之外,其他对象都不能访问这些状态信息;

3) 简化了发起者类,使其无需管理和保存其内部状态的各个备份,所有状态信息都保存在备忘录中,并由管理者进行管理;

2. 缺点

1) 资源消耗大,如果要保存的内部状态过多或特别繁琐,将会占用较大的内存资源;

2) 如果需要保存的属性发生了变化,那么我们还需要修改自己的代码

三. 应用场景

  1. 需要保存与恢复数据的场景,如玩游戏的中间结构的存档功能;

  2. 需要提供一个可回滚操作的场景,如Word、Eclipse等软件在编辑时 按 Ctrl+Z 组合键,还有数据库中的事务操作;

  3. 当一个对象需要记录其历史属性,并且需要记录的属性是所有属性的一部分时,可以使用备忘录模式记录属性;

  4. 为了节约内存,备忘录模式可以和原型模式配合使用 配合深拷贝;

四. 模式的结构

1.备忘录模式结构图

2.备忘录模式角色及职责

  1. Originator(发起者) : 记录当前时刻的内部状态信息,提供创建备忘录和恢复备忘录数据的功能,是需要保存状态的对象;

  2. Memento(备忘录) : 负责保存好记录,即Originator 内部状态,在需要的时候提供这些内部状态给发起者;

  3. Caretaker(管理者) : 对备忘录对象进行管理,提供保存与获取备忘录的功能,但不能对备忘录的内容进行访问与修改;

  4. 如果希望保存多个originator对象的不同时间的状态,可以使用HashMap<String, List<Memento>> 进行管理;

五. 模式的实现

0. 需求-游戏角色状态恢复问题

游戏角色有攻击力和防御力,在大战Boss前保存自身攻防状态,当大战Boss后攻防下降,这时从备忘录对象恢复到大战前的状态;

1.传统方案解决

类图 及 问题如下:

1) 一个对象就对应一个保存对象状态的对象,这样当我们游戏的对象很多时,不利于管理,内存开销也很大;

2) 传统方式是简单地做备份,new出另一个对象出来,再把需要备份的数据放到这个新对象中备份, 这样就暴露了对象内部的细节;

2.使用备忘录模式

1>实例结构图

2>流程说明

使用了备忘录模式之后,对方在提供给我们Orginator类的基础上还给我们提供了Memento、Caretaker类。我们客户端只需在需要备份的地方调用createMementor()函数,需要还原的地方调用recoverFromMemento(memento:Memento)。如果需要备份的属性发生了变化,那也只是第三方类库Orginator、Mementor进行了修改,对于我们客户端来说,代码不需要任何修改。

3>相关代码实现

/**
 * 备忘录模式客户端
 * @author NorthStar
 * @date 2021/12/18 20:39
 */
object MementoClient {
    @JvmStatic
    fun main(args: Array<String>) {
        val gameRole = GameRole()
        val caretaker = Caretaker()
        gameRole.vit=100
        gameRole.def=100
        println("和boss大战前的状态")
        gameRole.display()
        caretaker.setRoleMemento(gameRole.createMemento()) //保存状态
        println("和boss开始大战~")
        gameRole.vit=30
        gameRole.def=30
        println("和boss大战后的状态")
        gameRole.display()
        println("大战后 使用备忘录恢复到大战前~")
        gameRole.recoverGameRoleFromMemento(caretaker.getRoleMemento())
        println("恢复后的状态~")
        gameRole.display()
    }
}
/**
 * 备忘录角色
 * vit 攻击力
 * def 防御力
 */
class Memento(var vit:Int=0,var def:Int=0)/**


// 游戏角色 -Originator
class GameRole {
    var vit:Int=0
    var def:Int=0
    //创建 Memento 根据当前状态得到 Memento 对象
    fun createMemento():Memento{
        return Memento(vit,def)
    }
    //通过备忘录对象,恢复gameRole状态
    fun recoverGameRoleFromMemento(memento:Memento){
        vit=memento.vit
        def=memento.def
    }
    //显示当前游戏角色的状态
    fun display(){
        println("游戏角色当前的攻击力: $vit 防御力: $def")
    }
}
/**
 * 守护者对象, 保存游戏角色状态
 */
class Caretaker {
    //若对GameRole 只保存一次状态
    lateinit var memento: Memento


    //若对GameRole 需要保存多次
    var mementoList: ArrayList<Memento> = ArrayList()


    //若对多个游戏角色保存多个状态
    var roleMementoMap: HashMap<String, ArrayList<Memento>> = HashMap()


    fun add(memento: Memento) {
        mementoList.add(memento)
    }


    //获取第index个 Originator 的备忘录对象(即保存状态)
    fun getMemento(index: Int): Memento {
        return mementoList[index]
    }


    fun setRoleMemento(memento: Memento) {
        this.memento=memento
    }


    fun getRoleMemento(): Memento {
        return memento
    }
}

程序运行结果

和boss大战前的状态
游戏角色当前的攻击力: 100 防御力: 100
和boss开始大战~
和boss大战后的状态
游戏角色当前的攻击力: 30 防御力: 30
大战后 使用备忘录恢复到大战前~
恢复后的状态~
游戏角色当前的攻击力: 100 防御力: 100

六.备忘录模式与克隆的区别

有人说,备忘录模式就是用来保存对象中一些属性,那完全可以克隆这个对象,何必采用构造复杂的备忘录模式呢?原因有以下几点:

  1. 进行一次克隆会将对象的全部属性都复制到一个新的对象中去,而当我们仅需备份对象中一部分属性时使用备忘录模式最高效;

  2. 使用备忘录模式,当需要备份的属性发生变化后,只需修改Orginator(发起类)和Mementor(备忘录类),无需修改客户端代码;

  3. 使用备忘录模式,备份数据虽交给了其他对象保存,但对象的备份和还原操作都是通过对象本身函数实现的,体现了封装的思想;

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值