Java设计模式-备忘录模式

备忘录模式

      • 一、概述
      • 二、结构
      • 三、案例实现
        • (一)“白箱”备忘录模式
        • (二)“黑箱”备忘录模式
      • 四、优缺点
      • 五、使用场景

一、概述

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

定义:

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

二、结构

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

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

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

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

三、案例实现

【例】游戏挑战BOSS

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

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

  • “白箱”备忘录模式
  • “黑箱”备忘录模式
(一)“白箱”备忘录模式

备忘录角色对任何对象都提供一个接口,即宽接口,备忘录角色的内部所存储的状态就对所有对象公开。类图如下:

在这里插入图片描述

代码如下:

//游戏角色类
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);}public int getVit() {return vit;}public void setVit(int vit) {this.vit = vit;}public int getAtk() {return atk;}public void setAtk(int atk) {this.atk = atk;}public int getDef() {return def;}public void setDef(int def) {this.def = def;}
}//游戏状态存储类(备忘录类)
public class RoleStateMemento {private int vit;private int atk;private int def;public RoleStateMemento(int vit, int atk, int def) {this.vit = vit;this.atk = atk;this.def = def;}public int getVit() {return vit;}public void setVit(int vit) {this.vit = vit;}public int getAtk() {return atk;}public void setAtk(int atk) {this.atk = atk;}public int getDef() {return def;}public void setDef(int def) {this.def = def;}
}//角色状态管理者类
public class RoleStateCaretaker {private RoleStateMemento roleStateMemento;public RoleStateMemento getRoleStateMemento() {return roleStateMemento;}public void setRoleStateMemento(RoleStateMemento roleStateMemento) {this.roleStateMemento = roleStateMemento;}
}//测试类
public class Client {public static void main(String[] args) {System.out.println("------------大战Boss前------------");//大战Boss前GameRole gameRole = new GameRole();gameRole.initState();gameRole.stateDisplay();//保存进度RoleStateCaretaker roleStateCaretaker = new RoleStateCaretaker();roleStateCaretaker.setRoleStateMemento(gameRole.saveState());System.out.println("------------大战Boss后------------");//大战Boss时,损耗严重gameRole.fight();gameRole.stateDisplay();System.out.println("------------恢复之前状态------------");//恢复之前状态gameRole.recoverState(roleStateCaretaker.getRoleStateMemento());gameRole.stateDisplay();}
}

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

(二)“黑箱”备忘录模式

备忘录角色对发起人对象提供一个宽接口,而为其他对象提供一个窄接口。在Java语言中,实现双重接口的办法就是将备忘录类设计成发起人类的内部成员类。

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

在这里插入图片描述

代码如下:

窄接口Memento,这是一个标识接口,因此没有定义出任何的方法

public interface Memento {
}

定义发起人类 GameRole,并在内部定义备忘录内部类 RoleStateMemento(该内部类设置为私有的)

/游戏角色类
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);}public int getVit() {return vit;}public void setVit(int vit) {this.vit = vit;}public int getAtk() {return atk;}public void setAtk(int atk) {this.atk = atk;}public int getDef() {return def;}public void setDef(int def) {this.def = def;}private class RoleStateMemento implements Memento {private int vit;private int atk;private int def;public RoleStateMemento(int vit, int atk, int def) {this.vit = vit;this.atk = atk;this.def = def;}public int getVit() {return vit;}public void setVit(int vit) {this.vit = vit;}public int getAtk() {return atk;}public void setAtk(int atk) {this.atk = atk;}public int getDef() {return def;}public void setDef(int def) {this.def = def;}}
}

负责人角色类 RoleStateCaretaker 能够得到的备忘录对象是以 Memento 为接口的,由于这个接口仅仅是一个标识接口,因此负责人角色不可能改变这个备忘录对象的内容

//角色状态管理者类
public class RoleStateCaretaker {private Memento memento;public Memento getMemento() {return memento;}public void setMemento(Memento memento) {this.memento = memento;}
}

客户端测试类

public class Client {public static void main(String[] args) {System.out.println("------------大战Boss前------------");//大战Boss前GameRole gameRole = new GameRole();gameRole.initState();gameRole.stateDisplay();//保存进度RoleStateCaretaker roleStateCaretaker = new RoleStateCaretaker();roleStateCaretaker.setMemento(gameRole.saveState());System.out.println("------------大战Boss后------------");//大战Boss时,损耗严重gameRole.fight();gameRole.stateDisplay();System.out.println("------------恢复之前状态------------");//恢复之前状态gameRole.recoverState(roleStateCaretaker.getMemento());gameRole.stateDisplay();}
}

四、优缺点

1,优点:

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

2,缺点:

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

五、使用场景

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/237346.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

redis高级篇之单线程和多线程

目录 1、redis的发展史 2、redis为什么选择单线程? 3、主线程和Io线程是怎么协作完成请求处理的? 4、IO多路复用 5、开启redis多线程 1、redis的发展史 Redis4.0之前是用的单线程,4.0以后逐渐支持多线程 Redis4.0之前一直采用单线程的主…

[Linux 进程(三)] 进程优先级,进程间切换,main函数参数,环境变量

文章目录 1、进程优先级1.1 Linux下查看进程优先级1.2 Linux 进程优先级的修改PRI and NItop命令配合操作更改优先级 1.3 竞争 独立 并行 并发 2、进程间切换3、Linux2.6内核进程调度队列3.1 活跃进程3.2 过期进程 4 main函数参数 — 命令行参数4.1 利用main函数的参数实现一个…

132基于matlab的采集信号模极大值以及李氏指数计算

基于matlab的采集信号模极大值以及李氏指数计算, 1)计算信号的小波变换。 2)求出模极大曲线。 3)计算其中两个奇异点的Lipschitz指数,程序已调通,可直接运行。 132matlab模极大曲线Lipschitz (xiaohongshu.com)

Linux驱动学习—输入子系统

1、什么是输入子系统? 输入子系统是Linux专门做的一套框架来处理输入事件的,像鼠标,键盘,触摸屏这些都是输入设备,但是这邪恶输入设备的类型又都不是一样的,所以为了统一这些输入设备驱动标准应运而生的。…

关于lombok插件的使用

在 idea 中有个非常好用的插件 lombok,可以用来在实体类中自动生成 get 、set以及构造方法,下面我们来学习如何使用它: 首先打开settings,按照以下方法: 到 marketplace 中搜索 lombok,我这里已经安装好了…

9.5.1 函数模板特化

函数模板 有了泛化版本比较函数&#xff0c;我们可以比较两个整数&#xff0c;两个字符&#xff0c;两个指针 6~10行&#xff0c;是一个函数模板 13~16行&#xff0c;都可以得到正常结果 22行&#xff0c;得到的结果是&#xff0c;"A001" < "A000", …

RAG(检索增强生成 )

&#x1f4d1;前言 本文主要是【RAG】——RAG(检索增强生成 )的文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是听风与他&#x1f947; ☁️博客首页&#xff1a;CSDN主页听风与他 &#x1f304;每日一句…

springcloud sleuth分布式请求链路跟踪

简介 在微服务框架中&#xff0c;一个由客户端发起的请求在后端系统中会经过多个不同的的服务节点调用来协同产生最后的请求结果&#xff0c;每一个前段请求都会形成一条复杂的分布式服务调用链路&#xff0c;链路中的任何一环出现高延时或错误都会引起整个请求最后的失败. S…

抖店只做商品卡流量能行吗?为什么我的店铺没流量?出单玩法解析

我是王路飞。 抖店开通后&#xff0c;如果只做商品卡流量&#xff0c;可行吗&#xff1f; 有的新手商家就是不想给达人分佣金&#xff0c;就一直坚持做商品卡流量&#xff0c;但店铺却一直没什么流量&#xff0c;自然也就没出单了。 自然流量也是可以出单的&#xff0c;只能…

【MySQL】本地创建MySQL数据库详解

文章目录 下载MySQL安装重置密码本地连接 下载MySQL 下载网址&#xff1a;https://dev.mysql.com/downloads/mysql/ 安装 将下载好的压缩包解压到D盘。 在解压好的文件夹中创建my.ini文件。 将以下代码复制粘贴到创建好的my.ini文件中。注意修改文件路径。 [mysqld] #设置…

【SQL注入】SQLMAP v1.7.11.1 汉化版

下载链接 【SQL注入】SQLMAP v1.7.11.1 汉化版 简介 SQLMAP是一款开源的自动化SQL注入工具&#xff0c;用于扫描和利用Web应用程序中的SQL注入漏洞。它在安全测试领域被广泛应用&#xff0c;可用于检测和利用SQL注入漏洞&#xff0c;以验证应用程序的安全性。 SQL注入是一种…

Docker 安装:在linux系统CentOS7 版本 安装Docker

目录 一&#xff0c;Docker介绍&#xff1a; 1.1Docker是什么&#xff1f; 1.2Docker组成 二&#xff0c;Docker安装&#xff1a; 三&#xff0c;Docker基本使用 3.1服务 3.2镜像 3.3容器 &#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&am…

让网页自动化测试更简便,流程图设计工具为您解决痛点

在数字化时代&#xff0c;网页自动化测试已经成为提高工作效率、保证项目质量的重要手段。然而&#xff0c;传统的自动化测试往往需要复杂的编程技能&#xff0c;对非专业人员来说门槛较高。为了解决这个问题&#xff0c;我们向您推荐一款创新的设计工具&#xff0c;它可以通过…

jenkins环境搭建

jenkins环境搭建 1.环境说明2.环境准备1.jdk安装2.安装Git3.安装sshpass4.安装Maven 3.安装Jenkins(war包方式安装)1.安装2.镜像加速 4.官网提供的yum方式安装5.访问 1.环境说明 keyvalue环境centos7jdk版本11git2.43.0maven3.9.6jenkins最新版本http://mirrors.jenkins-ci.or…

nginx(1.13.7)首次安装出现:【make: *** 没有规则可以创建“default”需要的目标“build” 问题】解决措施

目录 前言&#xff1a; 一.龙蜥&#xff08;Anolis&#xff09;操作系统上安装GCC 1.安装gcc 2.检验安装 二.安装出现 make&#xff1a; *** 没有规则可以创建“default”需要的目标“build” 问题 1.解压安装nginx 2.安装出现问题展示 3.解决措施 4.重新编译进行安装 5…

算法第十七天-构造有效字符串的最少插入数

构造有效字符串的最少插入数 题目要求 解题思路 考虑abc的个数 假设答案有n个"abc"组成&#xff0c;那么需要插入的字符个数为 3 ∗ n − l e n ( s ) 3*n - len(s) 3∗n−len(s)。 对于相邻的两个字符x和y&#xff08;x在y左侧&#xff09;&#xff1a; 如果 x…

ubuntu安装node

1 下载 node 官网下载 如果需要其他版本&#xff0c;点击上图的Other Downloads 这里下载的版本是20.11.0 Linux Binaries (x64)&#xff0c;下载下来后是node-v20.11.0-linux-x64.tar.xz这样的格式&#xff0c;直接右键解压得到如下目录&#xff1a; 直接拷贝该文件夹到指定目…

通过IP地址识别风险用户

随着互联网的迅猛发展&#xff0c;网络安全成为企业和个人关注的焦点之一。识别和防范潜在的风险用户是维护网络安全的关键环节之一。IP数据云将探讨通过IP地址识别风险用户的方法和意义。 IP地址的基本概念&#xff1a;IP地址是互联网上设备的独特标识符&#xff0c;它分为IP…

redis学习

redis的五个基本数据类型。 redis字符串&#xff08;String&#xff09; string是redis最基本的类型&#xff0c;一个key对应一个Tvalue。 string类型是二进制安全的&#xff0c;意思是redis的string可以包含任何数据&#xff0c;比如jpg图片或者序列化的对象。 string类型是…

vue项目之.env文件.env.dev、test、pro

.env文件是vue运行项目时的环境配置文件。 .env: 全局默认配置文件&#xff0c;所有环境(开发、测试、生产等&#xff09;均会加载并合并该文件 .env.development(开发环境默认命名) 开发环境的配置&#xff0c;文件名默认为.env.development,如果需要改名也是可以的&#xf…