备忘录模式

  • 6.10 备忘录模式
    • 6.10.1 概述
    • 6.10.2 结构
    • 6.10.3 案例实现
      • “白箱” 备忘录模式
      • “黑箱” 备忘录模式
    • 6.10.4 优缺点
    • 6.10.5 使用场景

完整的笔记目录:《设计模式详解》笔记目录,欢迎指点!

行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象无法单独完成的任务,它涉及算法与对象间职责的分配。行为型模式分为类行为模式和对象行为模式:

  • 类行为模式:采用继承机制来在类间分派行为
  • 对象行为模式:采用组合或聚合在对象间分配行为

由于组合关系或聚合关系比继承关系耦合度低,满足 “合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。

行为型模式分为:

  • 模板方法模式
  • 策略模式
  • 命令模式
  • 职责链模式
  • 状态模式
  • 观察者模式
  • 中介者模式
  • 迭代器模式
  • 访问者模式
  • 备忘录模式
  • 解释器模式

以上 11 种行为型模式,除了模板方法模式和解释器模式是类行为型模式,其他的全部属于对象行为型模式。

6.10 备忘录模式

6.10.1 概述

备忘录模式提供了一种状态恢复的实现机制,使得用户可以方便地回到一个特定的历史步骤,当新的状态无效或者存在问题时,可以使用暂时存储起来的备忘录将状态复原,很多软件都提供了撤销(Undo)操作,如 Word、记事本、Photoshop、IDEA等软件在编辑时按 Ctrl+Z 组合键时能撤销当前操作,使文档恢复到之前的状态;还有在 浏览器 中的后退键、数据库事务管理中的回滚操作、玩游戏时的中间结果存档功能、数据库与操作系统的备份操作、棋类游戏中的悔棋功能等都属于这类。

备忘录模式:又叫快照模式,在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态。

6.10.2 结构

备忘录模式的主要角色如下:

  • Originator:发起人角色,记录当前时刻的内部状态信息,提供创建备忘录和恢复备忘录数据的功能,实现其他业务功能,它可以访问备忘录里的所有信息。
  • Memento:备忘录角色,负责存储发起人的内部状态,在需要的时候提供这些内部状态给发起人。
  • Caretaker:管理者角色,对备忘录进行管理,提供保存与获取备忘录的功能,但其不能对备忘录的内容进行访问与修改。

备忘录有两个等效的接口:

  • 窄接口:管理者对象(和其他发起人对象之外的任何对象)看到的是备忘录的窄接口 (narror Interface),这个窄接口只允许他把备忘录对象传给其他的对象。
  • 宽接口:发起人对象可以看到一个宽接口 (wide Interface),这个宽接口允许它读取所有的数据,以便根据这些数据恢复这个发起人对象的内部状态。

6.10.3 案例实现

【例】游戏挑战BOSS

游戏中的某个场景,一游戏角色有生命力、攻击力、防御力等数据,在打 Boss 前和后一定会不一样的,我们允许玩家如果感觉与 Boss 决斗的效果不理想可以让游戏恢复到决斗之前的状态。

要实现上述案例,有两种方式:

  • “白箱” 备忘录模式
  • “黑箱” 备忘录模式

“白箱” 备忘录模式

白箱模式下,备忘录角色对任何对象都提供一个宽接口,备忘录角色的内部所存储的状态就对所有对象公开。

发起人角色:游戏角色

/*** 游戏角色类(属于发起人角色)*/
@Data
public class GameRole {private int vit; // 生命力private int atk; // 攻击力private int def; // 防御力// 初始化内部状态public void initState() {this.vit = 100;this.atk = 100;this.def = 100;}// 战斗public void fight() {this.vit = 0;this.atk = 0;this.def = 0;}// 保存角色状态功能public RoleStateMemento saveState() {return new RoleStateMemento(vit, atk, def);}// 恢复角色状态public void recoverState(RoleStateMemento roleStateMemento) {// 将备忘录对象中存储的状态赋值给当前对象的成员this.vit = roleStateMemento.getVit();this.atk = roleStateMemento.getAtk();this.def = roleStateMemento.getDef();}// 展示状态功能public void stateDisplay() {System.out.println("角色生命力:" + vit);System.out.println("角色攻击力:" + atk);System.out.println("角色防御力:" + def);}
}

备忘录角色:用于存储发起人的内部状态,因此拥有属性和发起人一样

@Data
@AllArgsConstructor
public class RoleStateMemento {private int vit; // 生命力private int atk; // 攻击力private int def; // 防御力
}

管理者角色:用于管理备忘录角色

@Data
public class RoleStateCaretaker {private RoleStateMemento roleStateMemento;
}

测试类:

public class Client {public static void main(String[] args) {System.out.println("---------------大战boss前-----------------");// 创建游戏角色对象GameRole gameRole = new GameRole();gameRole.initState(); // 初始化状态操作gameRole.stateDisplay();// 将该游戏角色内部状态进行备份// 创建管理者对象RoleStateCaretaker roleStateCaretaker = new RoleStateCaretaker();roleStateCaretaker.setRoleStateMemento(gameRole.saveState());System.out.println("---------------大战boss后-----------------");// 损耗严重gameRole.fight();gameRole.stateDisplay();System.out.println("---------------恢复之前的状态-----------------");gameRole.recoverState(roleStateCaretaker.getRoleStateMemento());gameRole.stateDisplay();}
}
---------------大战boos前-----------------
角色生命力:100
角色攻击力:100
角色防御力:100
---------------大战boos后-----------------
角色生命力:0
角色攻击力:0
角色防御力:0
---------------恢复之前的状态-----------------
角色生命力:100
角色攻击力:100
角色防御力:100

分析:白箱备忘录模式是破坏封装性的,但是通过程序员自律,同样可以在一定程度上实现模式的大部分用意。

“黑箱” 备忘录模式

黑箱模式下,备忘录角色对发起人对象提供一个宽接口,为其他对象提供一个窄接口。

Java 中,实现双重接口的办法是:将备忘录类设计成发起人类的内部成员类。

  • RoleStateMemento 设为 GameRole 的内部类,从而将 RoleStateMemento 对象封装在 GameRole 里面;
  • 在外面提供一个标识接口 MementoRoleStateCaretaker 及其他对象使用。
  • 这样 GameRole 类看到的是 RoleStateMemento 所有的接口,而RoleStateCaretaker 及其他对象看到的仅仅是标识接口 Memento 所暴露出来的接口,从而维护了封装性。

备忘录接口:对外提供窄接口,标识接口,没有任何方法

public interface Memento {}

发起人角色:游戏角色,在内部定义备忘录内部类 RoleStateMemento(私有),对自己提供宽接口,但外部无法访问

@Data
public class GameRole {private int vit; // 生命力private int atk; // 攻击力private int def; // 防御力// 初始化内部状态public void initState() {this.vit = 100;this.atk = 100;this.def = 100;}// 战斗public void fight() {this.vit = 0;this.atk = 0;this.def = 0;}// 保存角色状态功能public Memento saveState() {return new RoleStateMemento(vit, atk, def);}// 恢复角色状态public void recoverState(Memento memento) {RoleStateMemento roleStateMemento = (RoleStateMemento) memento;// 将备忘录对象中存储的状态赋值给当前对象的成员this.vit = roleStateMemento.getVit();this.atk = roleStateMemento.getAtk();this.def = roleStateMemento.getDef();}// 展示状态功能public void stateDisplay() {System.out.println("角色生命力:" + vit);System.out.println("角色攻击力:" + atk);System.out.println("角色防御力:" + def);}// 对发起者宽接口,对其他类窄接口@Data@AllArgsConstructorprivate static class RoleStateMemento implements Memento {private int vit; // 生命力private int atk; // 攻击力private int def; // 防御力}
}

管理者角色:这个类聚合的是 Memento 接口,只是个标识接口,因此该角色无法更改备忘录的内容

@Data
public class RoleStateCaretaker {private Memento memento;
}

测试类:

public class Client {public static void main(String[] args) {System.out.println("---------------大战boos前-----------------");// 创建游戏角色对象GameRole gameRole = new GameRole();gameRole.initState(); // 初始化状态操作gameRole.stateDisplay();// 将该游戏角色内部状态进行备份// 创建管理者对象RoleStateCaretaker roleStateCaretaker = new RoleStateCaretaker();roleStateCaretaker.setMemento(gameRole.saveState());System.out.println("---------------大战boos后-----------------");// 损耗严重gameRole.fight();gameRole.stateDisplay();System.out.println("---------------恢复之前的状态-----------------");gameRole.recoverState(roleStateCaretaker.getMemento());gameRole.stateDisplay();}
}
---------------大战boos前-----------------
角色生命力:100
角色攻击力:100
角色防御力:100
---------------大战boos后-----------------
角色生命力:0
角色攻击力:0
角色防御力:0
---------------恢复之前的状态-----------------
角色生命力:100
角色攻击力:100
角色防御力:100

6.10.4 优缺点

优点:

  • 提供了一种可以恢复状态的机制。当用户需要时能够比较方便地将数据恢复到某个历史的状态。
  • 简化了发起人类。发起人无需管理和保存其内部状态的备份,所有状态备份保存在备忘录中,由管理者进行管理,符合单一职责原则。
  • (黑箱模式)实现了内部状态的封装。除了创建它的发起人之外,其他对象都不能够访问这些状态信息。

缺点:

  • 资源消耗大。如果要保存的内部状态信息过多或者特别频繁,将会占用比较大的内存资源。

6.10.5 使用场景

  • 需要保存与恢复数据的场景,如玩游戏时的中间结果的存档功能。

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

《设计模式详解》行为型模式 - 备忘录模式相关推荐

  1. JAVA设计模式详解(六)----------状态模式

    各位朋友,本次LZ分享的是状态模式,在这之前,恳请LZ解释一下,由于最近公司事情多,比较忙,所以导致更新速度稍微慢了些(哦,往后LZ会越来越忙=.=). 状态模式,又称状态对象模式(Pattern o ...

  2. 设计模式详解(四)--------建造者模式

    一.定义 将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示,这样的设计模式被称为建造者模式. 二.使用范围 1.创建一个复杂的对象,他有多个不同的模块组成,其中有些模块不会改变, ...

  3. 设计模式详解(五)——建造者模式

    一.场景问题 大家很多人应该都玩过LOL(英雄联盟)这款游戏,博主从S4赛季一直玩到现在,可以说这块游戏陪伴了我整个大学生涯.正好现在S10入围赛已经快接近尾声,即将到精彩的小组赛了.当前这篇文章不是 ...

  4. 《设计模式详解》创建型模式 - 工厂模式

    <设计模式详解> 4.2 工厂模式 4.2.1 概述 4.2.2 简单工厂模式 4.2.2.1 结构 4.2.2.2 实现 4.2.2.3 优缺点 4.2.2.4 扩展 - 静态工厂 4. ...

  5. 设计模式详解(四)抽象工厂模式

    文章目录 1. 简介 2. 代码实例 3. 抽象工厂的优缺点 1. 简介 定义:为创建一组相关或相互依赖的对象提供一个接口,而且无须指定它们的具体类 与工厂方法模式不同(工厂方法的升级,在工厂方法模式 ...

  6. 《设计模式详解》行为型模式 - 解释器模式

    解释器模式 6.11 解释器模式 6.11.1 概述 6.11.2 结构 6.11.3 案例实现 6.11.4 优缺点 6.11.5 使用场景 完整的笔记目录:<设计模式详解>笔记目录,欢 ...

  7. 《设计模式详解》行为型模式 - 观察者模式

    观察者模式 6.6 观察者模式 6.6.1 概述 6.6.2 结构 6.6.3 案例实现 6.6.4 优缺点 6.6.5 使用场景 6.6.6 JDK 提供的实现 - Observable 示例 完整 ...

  8. 《设计模式详解》行为型模式 - 状态模式

    状态模式 6.5 状态模式 6.5.1 反例 6.5.2 结构 6.5.3 案例实现 6.5.4 优缺点 6.5.5 使用场景 完整的笔记目录:<设计模式详解>笔记目录,欢迎指点! 行为型 ...

  9. 《设计模式详解》行为型模式 - 职责链模式

    职责链模式 6.4 职责链模式 6.4.1 概述 6.4.2 结构 6.4.3 案例实现 6.4.4 优缺点 6.4.5 JavaWeb 源码 - FilterChain 完整的笔记目录:<设计 ...

最新文章

  1. 【Android Gradle 插件】ProductFlavor 配置 ( applicationId 配置 | SdkVersion 相关配置 | version 应用版本配置 )
  2. Socket基础API介绍
  3. internet网络 checksum校验和计算方法
  4. Linux基础(iptables与firewalld防火墙)
  5. Java数据库篇1——数据库配置
  6. android 底部停靠 底部吸附_android让xml布局的底部跟随软键盘
  7. 听说当今程序员很厉害?不,那是你不了解上古时期的那些神级操作
  8. 基于SSH2做一个24小时订单分析表格
  9. objective-C 2.0
  10. 如何提高go代码覆盖率_如何提高代码质量?
  11. MapReduce WordCount Combiner程序
  12. BeanFactory和FactoryBean区别
  13. ios共享账号公众号_新增iOS应用账号共享09
  14. 执行maven install命令报java不支持diamond语法
  15. [源码分析]go-graceful如何shutdown gracefully
  16. 2db多少功率_功率换算(dB与W).doc
  17. 杀猪盘、仙人跳…手法过于大开眼界!“反欺诈AI大赛来了
  18. 安装VMware虚拟机后,网络适配器找不到VMnet8和VMnet1解决方法。
  19. 如何在新环境接手项目?(上)【洞见2】
  20. 【网安神器篇】——enum4linux枚举工具

热门文章

  1. 互‮网联‬上什么人可以‮大赚‬钱?
  2. 现在新电脑主板带WiFi,实不实用,稳定吗?
  3. 将内存使用的详细情况输出到文件
  4. 单线程reactor
  5. sql server 监视_监视SQL Server报告服务
  6. sql azure 语法_如何:Azure中SQL Server文件快照备份
  7. 使用xp_readerrorlog命令读取SQL Server错误日志
  8. sql备份恢复数据库_使用DBATools通过SQL恢复数据库操作验证备份
  9. 剑指offer.删除链表中重复的节点
  10. python学习笔记(对象)