行为型设计模式05-备忘录模式

news2024/11/26 20:30:49

🧑‍💻作者:猫十二懿

❤️‍🔥账号:CSDN 、掘金 、个人博客 、Github

🎉公众号:猫十二懿

备忘录模式

1、备忘录模式介绍

备忘录模式是一种行为型设计模式,用于在不破坏封装性的前提下保存和恢复对象的状态。该模式通常包括三个角色:发起者(Originator)、备忘录(Memento)和管理者(Caretaker)。

1.1 存在问题

如果在工作中不使用备忘录模式,可能会导致一些问题。比如,当我们需要保存程序或对象的某个状态以便在将来某个时刻恢复时,如果我们没有合适的机制来管理状态,那么就很难实现这样的需求。此时,我们可能需要手动记录状态并将其存储到文件或数据库中,同时还需要管理和维护状态的版本控制,这会很耗费时间和精力。而且,如果我们的程序需要支持多种状态的保存和恢复,那么这个过程会变得更加困难。

因此,在这种情况下,使用备忘录模式可以很好地解决这个问题。它提供了一个简单而有效的方法来捕获和存储对象的内部状态,从而使我们可以在需要时轻松地恢复对象的状态。同时,备忘录模式也有助于保持封装性,因为对象的状态仅限于它自己和备忘录对象之间共享,其他对象无法访问或更改状态。

  1. 例如

假设你正在为公司编写一个重要的软件程序,这个程序中包含了一些复杂的业务逻辑。你需要测试和修改这个程序,但是由于代码比较复杂,你很难在第一次测试时分辨出哪些代码有问题,哪些代码没有问题。如果此时你不使用备忘录模式来保存程序的某个状态以便在将来某个时刻回溯到之前的状态,那么你每次都需要重新验证每一段代码的正确性,这会非常浪费时间和精力,并且有可能漏掉一些问题。

另外,如果你在修改程序时不小心破坏了某些核心代码,而此时你没有任何备份或历史记录,那么整个程序就可能遭受损失,这也是没有使用备忘录模式会存在的问题。而如果你使用备忘录模式,就可以轻松地恢复到之前的某个状态,避免了这种风险。

  1. 再例如

假设一个学生正在上课,突然需要离开教室一段时间,但他不希望错过老师所讲解的内容。如果他没有任何方法来记录老师的讲解,那么他在回来之后就无法恢复自己的知识状态,可能会影响以后的学习。但是,如果他能够使用备忘录模式来记录老师的讲解,就可以轻松地恢复到离开教室前的知识状态。

1.2 备忘录模式

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

image-20230508230000293

  1. Originator(发起人):负责创建一个备忘录Memento,用以记录当 前时刻它的内部状态,并可使用备忘录恢复内部状态。Originator 可根据需要决定Memento存储Originator的哪些内部状态。
  2. Memento(备忘录):负责存储Originator对象的内部状态,并可防 止Originator以外的其他对象访问备忘录Memento。备忘录有两个接 口,Caretaker只能看到备忘录的窄接口,它只能将备忘录传递给其 他对象。Originator能够看到一个宽接口,允许它访问返回到先前 状态所需的所有数据。
  3. Caretaker(管理者):负责保存好备忘录Memento,不能对备忘录 的内容进行操作或检查。

1.3 备忘录模式基本代码

备忘录类:

/**
 * @author Shier
 * CreateTime 2023/5/10 11:31
 * 备忘录类
 */
public class Memento {
    private String state;

    public Memento(String state) {
        this.state = state;
    }

    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state;
    }
}

发起人(初始)类:

/**
 * @author Shier
 * CreateTime 2023/5/10 11:26
 * 发起人(Originator)类
 */
public class Originator {
    /**
     * 状态
     */
    private String state;

    /**
     * 显示数据
     */
    public void show() {
        System.out.println("Current State : " + this.state);
    }

    /**
     * 创建备忘录
     *
     * @return 将当前需要保存的信息导入并实例化出一个Memento对象
     */
    public Memento createMemento(){
        return new Memento(this.state);
    }
    /**
     * 恢复备忘录
     */
    public void recoveryMemento(Memento memento){
        this.setState(memento.getState());
    }

    /**
     * 需要保存的属性
     *
     * @return
     */
    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state;
    }
}

管理类:

/**
 * @author Shier
 * CreateTime 2023/5/10 11:35
 * 管理类
 */
public class Caretaker {
    private Memento memento;

    public Memento getMemento() {
        return memento;
    }

    public void setMemento(Memento memento) {
        this.memento = memento;
    }
}

客户端类:

/**
 * @author Shier
 * CreateTime 2023/5/10 11:36
 */
public class MementoClient {
    public static void main(String[] args) {
        // 原始类初始化
        Originator originator = new Originator();
        originator.setState("ON");
        originator.show();

        // 保存当前状态
        Caretaker caretaker = new Caretaker();
        caretaker.setMemento(originator.createMemento());

        // 状态进行改变
        originator.setState("OFF");
        originator.show();

        // 恢复初始的状态
        originator.recoveryMemento(caretaker.getMemento());
        originator.show();
    }
}

输出结果:

image-20230510114047565

2、具体例子说明

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

初始状态:

image-20230510114351823

战斗后:

image-20230510114430116

2.1 不使用备忘录模式

游戏角色:

/**
 * @author Shier
 * CreateTime 2023/5/10 11:45
 * 游戏角色类
 */
public class GameRole {
    /**
     * 生命力
     */
    private int vitality;

    /**
     * 攻击力
     */
    private int attack;

    /**
     * 防御力
     */
    private int defense;


    /**
     * 状态显示
     */
    public void displayState() {
        System.out.println("角色当前状态:");
        System.out.println("生命力:" + this.vitality);
        System.out.println("攻击力:" + this.attack);
        System.out.println("防御力:" + this.defense);
        System.out.println();
    }

    /**
     * 初始状态
     *
     * @return
     */
    public void getInitState() {
        this.vitality = 100;
        this.attack = 100;
        this.defense = 100;
    }

    /**
     * 战斗后状态
     *
     * @param
     */
    public void fight() {
        this.vitality = 0;
        this.attack = 0;
        this.defense = 0;
    }

    public int getVitality() {
        return vitality;
    }


    public void setVitality(int vitality) {
        this.vitality = vitality;
    }

    public int getAttack() {
        return attack;
    }

    public void setAttack(int attack) {
        this.attack = attack;
    }

    public int getDefense() {
        return defense;
    }

    public void setDefense(int defense) {
        this.defense = defense;
    }
}

客户端:

/**
 * @author Shier
 * CreateTime 2023/5/10 11:51
 */
public class GameClient {
    public static void main(String[] args) {
        // 战斗前
        GameRole gameRole = new GameRole();
        gameRole.getInitState();
        gameRole.displayState();

        // 保存进度
        GameRole backRole = new GameRole();
        backRole.setAttack(gameRole.getAttack());
        backRole.setDefense(gameRole.getDefense());
        backRole.setVitality(gameRole.getVitality());

        // 战斗
        gameRole.fight();
        gameRole.displayState();

        // 恢复战斗前状态
        gameRole.setVitality(backRole.getVitality());
        gameRole.setDefense(backRole.getDefense());
        gameRole.setAttack(backRole.getAttack());
        // 显示恢复后的状态
        gameRole.displayState();
    }
}

输出结果:

image-20230510120726488

分析

确实实现了例子的要求,问题主要在于这客户端的调用。下面的程序存在问题,因为这样写就把整个游戏角色的细节暴露给了客户端,客户端的职责就太大了,需要知道游戏角色的生命力、攻击力、防御力这些细节,还要对它进行’备份’。以后需要增加新的数据,例如增加 ‘魔法力’ 或修改现有的某种力,例如 ‘生命力’ 改为 ‘经验值’,这部分就一定要修改了。同样的道理也存在于恢复时的代码。

image-20230510115755695

解决:

显然,我们希望的是把这些’游戏角色’的存取状态细节封装起来,而且 最好是封装在外部的类当中。以体现职责分离。

2.2 使用备忘录模式实现

代码结构图:

image-20230510115841132

角色状态记录类:

/**
 * @author Shier
 * CreateTime 2023/5/10 11:59
 * 角色状态记录
 */
public class RoleStateMemento {
    /**
     * 生命力
     */
    private int vitality;

    /**
     * 攻击力
     */
    private int attack;

    /**
     * 防御力
     */
    private int defense;

    public RoleStateMemento(int vitality, int attack, int defense) {
        this.vitality = vitality;
        this.attack = attack;
        this.defense = defense;
    }

    public int getVitality() {
        return vitality;
    }


    public void setVitality(int vitality) {
        this.vitality = vitality;
    }

    public int getAttack() {
        return attack;
    }

    public void setAttack(int attack) {
        this.attack = attack;
    }

    public int getDefense() {
        return defense;
    }

    public void setDefense(int defense) {
        this.defense = defense;
    }
}

角色类:

/**
 * @author Shier
 * CreateTime 2023/5/10 11:45
 * 游戏角色类
 */
public class GameRole {
    /**
     * 生命力
     */
    private int vitality;

    /**
     * 攻击力
     */
    private int attack;

    /**
     * 防御力
     */
    private int defense;


    /**
     * 状态显示
     */
    public void displayState() {
        System.out.println("角色当前状态:");
        System.out.println("生命力:" + this.vitality);
        System.out.println("攻击力:" + this.attack);
        System.out.println("防御力:" + this.defense);
        System.out.println();
    }

    /**
     * 初始状态
     *
     * @return
     */
    public void getInitState() {
        this.vitality = 100;
        this.attack = 100;
        this.defense = 100;
    }

    /**
     * 战斗后状态
     *
     * @param
     */
    public void fight() {
        this.vitality = 0;
        this.attack = 0;
        this.defense = 0;
    }

    /**
     * 保存角色状态
     *
     * @return
     */
    public RoleStateMemento savaState(){
        return new RoleStateMemento(this.vitality,this.attack,this.defense);
    }

    /**
     * 恢复角色状态
     * @return
     */
    public void recoveryState(RoleStateMemento roleStateMemento){
        this.setAttack(roleStateMemento.getAttack());
        this.setDefense(roleStateMemento.getDefense());
        this.setVitality(roleStateMemento.getVitality());
    }

    public int getVitality() {
        return vitality;
    }


    public void setVitality(int vitality) {
        this.vitality = vitality;
    }

    public int getAttack() {
        return attack;
    }

    public void setAttack(int attack) {
        this.attack = attack;
    }

    public int getDefense() {
        return defense;
    }

    public void setDefense(int defense) {
        this.defense = defense;
    }
}

角色管理类:

/**
 * @author Shier
 * CreateTime 2023/5/10 12:04
 * 角色管理类
 */
public class RoleStateCaretaker {
    private RoleStateMemento roleStateMemento;

    public RoleStateMemento getRoleStateMemento() {
        return roleStateMemento;
    }

    public void setRoleStateMemento(RoleStateMemento roleStateMemento) {
        this.roleStateMemento = roleStateMemento;
    }
}

客户端类:

/**
 * @author Shier
 * CreateTime 2023/5/10 11:51
 */
public class GameClient2 {
    public static void main(String[] args) {
        // 战斗前
        GameRole role = new GameRole();
        role.getInitState();
        role.displayState();

        // 保存进度
        RoleStateCaretaker caretaker = new RoleStateCaretaker();
        caretaker.setRoleStateMemento(role.savaState());

        // 战斗
        role.fight();
        role.displayState();

        // 恢复战斗前状态
        role.recoveryState(caretaker.getRoleStateMemento());
        // 显示恢复后的状态
        role.displayState();
    }
}

输出结果同上

  1. 把要保存的细节给封装在了RoleStateMemento中了, 哪一天要更改保存的细节也不用影响客户端了。
  2. 使用备忘录可以把复杂的对象内部信息对其他的对象屏蔽起来
  3. 在当角色的状态改变的时候,有可能这个状态无效,这时候就可以使用暂时存储起来的备忘录将状态复原

3、总结

备忘录模式优点:

  1. 可以在不破坏对象封装性的前提下,捕获和恢复对象的内部状态;
  2. 可以对状态进行存储和恢复,保证程序的稳定性;
  3. 可以缩小“黑盒”对象和“白盒”对象之间的耦合度,提高程序的可扩展性和可维护性;
  4. 可以有效地管理多个历史状态,方便用户选择并恢复到特定的状态。

备忘录模式缺点:

  1. 对象状态的保存和恢复会消耗大量的内存,特别是针对大型对象的情况;
  2. 如果状态的保存和恢复涉及到较多的属性(或数据),则备忘录对象可能会非常庞大,导致程序效率低下。

备忘录模式使用场景:

  1. 需要保存和恢复对象状态的情况;
  2. 需要提供撤销操作的情况;
  3. 需要记录对象历史状态的情况;
  4. 需要动态地保存和恢复对象状态的情况;
  5. 功能比较复杂的,但需要维护或记录属性历史的类,或者需要保存的属性只是众多属性中的一小部分时,Originator可以根据保存的Memento信息还原到前一状态。

to中了, 哪一天要更改保存的细节也不用影响客户端了。

  1. 使用备忘录可以把复杂的对象内部信息对其他的对象屏蔽起来
  2. 在当角色的状态改变的时候,有可能这个状态无效,这时候就可以使用暂时存储起来的备忘录将状态复原

3、总结

备忘录模式优点:

  1. 可以在不破坏对象封装性的前提下,捕获和恢复对象的内部状态;
  2. 可以对状态进行存储和恢复,保证程序的稳定性;
  3. 可以缩小“黑盒”对象和“白盒”对象之间的耦合度,提高程序的可扩展性和可维护性;
  4. 可以有效地管理多个历史状态,方便用户选择并恢复到特定的状态。

备忘录模式缺点:

  1. 对象状态的保存和恢复会消耗大量的内存,特别是针对大型对象的情况;
  2. 如果状态的保存和恢复涉及到较多的属性(或数据),则备忘录对象可能会非常庞大,导致程序效率低下。

备忘录模式使用场景:

  1. 需要保存和恢复对象状态的情况;
  2. 需要提供撤销操作的情况;
  3. 需要记录对象历史状态的情况;
  4. 需要动态地保存和恢复对象状态的情况;
  5. 功能比较复杂的,但需要维护或记录属性历史的类,或者需要保存的属性只是众多属性中的一小部分时,Originator可以根据保存的Memento信息还原到前一状态。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Spring Resources资源操作

文章目录 1、Spring Resources概述2、Resource接口3、Resource的实现类3.1、UrlResource访问网络资源3.2、ClassPathResource 访问类路径下资源3.3、FileSystemResource 访问文件系统资源3.4、ServletContextResource3.5、InputStreamResource3.6、ByteArrayResource 4、Resour…

H桥级联型五电平三相逆变器MATLAB仿真模型

H桥级联型五电平逆变器MATLAB仿真模型资源-CSDN文库https://download.csdn.net/download/weixin_56691527/87899094 模型简介: MATLAB21b版本 逆变器采用H桥级联的形式连接,加设LCL滤波器,三相负载构成主电路。 采用SPWM调制,可…

不宜使用Selenium自动化的10个测试场景

尽管在很多情况下测试自动化是有意义的,但一些测试场景是不应该使用自动化测试工具的,比如Selenium、WebDriver。 下面有10个示例,来解释为什么自动化在这种情况下使用时没有意义的,我还将为您提供每种方法的替代方法。 01.验证…

TreeView 简单使用

本文主要介绍 QML 中 TreeView 的基本使用方法,包括:TreeView的适用场景; 控件简介 QML TreeView 是 Qt Quick 中的一个组件,用于显示树形结构的数据。它提供了一种以层次结构方式展示数据的方式,其中每个节点可以包含…

ESP32学习之定时器和PWM

一.定时器代码如下&#xff1a; #include <Arduino.h>hw_timer_t *timer NULL; int interruptCounter 0;// 函数名称&#xff1a;onTimer() // 函数功能&#xff1a;中断服务的功能&#xff0c;它必须是一个返回void&#xff08;空&#xff09;且没有输入参数的函数 //…

【动态规划】路径问题

冻龟算法系列之路径问题 文章目录 【动态规划】路径问题1. 不同路径1.1 题目解析1.2 算法原理1.2.1 状态表示1.2.2 状态转移方程1.2.3 初始化1.2.4 填表顺序1.2.5 返回值 1.3 编写代码 2. 不同路径Ⅱ2.1 题目解析2.2 算法原理2.2.1 状态表示2.2.2 状态转移方程2.2.3 初始化2.2.…

性能测试学习之数据驱动性能测试

了解数据驱动测试理念、能够如何在jmeter中用多种方式实现数据驱动测试。 知识点&#xff1a;字符串拼接、计数器、循环控制器 1. 数据驱动的理念 1.1 定义 从数据文件中读取测试数据,驱动测试过程的一-种测试方法数据驱动可以理解为更高级的参数化 1.2 特点 测试数据与测试…

【Linux】socket 编程(socket套接字介绍、字节序、socket地址、IP地址转换函数、套接字函数、TCP通信实现)

目录 1、socket套接字介绍2、字节序简介字节序转换函数 3、socket地址专用socket地址 4、IP地址转换函数5、套接字函数6、TCP通信实现&#xff08;服务器端和客户端&#xff09; 橙色 1、socket套接字介绍 所谓套接字&#xff0c;就是对网络中不同主机上的应用进程之间进行双…

深入理解深度学习——Transformer:整合编码器(Encoder)和解码器Decoder)

分类目录&#xff1a;《深入理解深度学习》总目录 相关文章&#xff1a; 注意力机制&#xff08;Attention Mechanism&#xff09;&#xff1a;基础知识 注意力机制&#xff08;Attention Mechanism&#xff09;&#xff1a;注意力汇聚与Nadaraya-Watson核回归 注意力机制&…

国内唯一可以在本地搭建Stable Diffusion WebUI教程-安装时无需魔法安装全程流畅到尖叫

Stable Diffusion是什么 Stable Diffusion简称SD是一款Ai图片生成工具。“输入几句话,生成精美图片。” 比如说我一开头这幅图片就是用的SD生成的。 我在我的“ChatGPT让我变成了“超人”-如何提升团队30%效能质量提高100%的阶段性总结报告”里提到过midjourney,但是midjou…

使用Google工具类Guava自定义一个@Limiter接口限流注解

在Springboot中引用RateLimiter工具类依赖 <dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>30.1-jre</version> </dependency> 需要注意的是&#xff0c;Guava 的不同版本可能会有…

新手第一次做性能测试?性能测试流程详全,从需求到报告一篇打通

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、确认需求 确定…

3、互联网行业及产品经理分类

上一篇文章&#xff1a;2、产品经理的工作内容_阿杰学编程的博客-CSDN博客 1、产品经理分类 我们把产品经理划分成这样两个大的类型&#xff0c;一个是传统行业的&#xff0c;一个是互联网行业的。这个简单了解一下就行。 这个里面会发现绝大多数也是体育劳动&#xff0c;你比…

软件测试岗位都是女孩子在做吗?

听我一朋友说&#xff0c;测试岗位基本都是女孩子做。” 不知道是不是以前“软件测试岗”给人印象是“不需要太多技术含量”的错觉&#xff0c;从而大部分外行认为从业软件测试的人员中女生应占了大多数。比如有人就觉得&#xff1a;软件测试主要是细心活&#xff0c;所以女生…

2023 年各大互联网公司常见面试题(Java 岗)汇总

很多人都说今年对于 IT 行业根本没有所谓的“金三银四”“金九银十”。在各大招聘网站或者软件上不管是大厂还是中小公司大多都是挂个招聘需求&#xff0c;实际并不招人&#xff1b;在行业内的程序员基本都已经感受到了任老前段时间口中所谓的“寒气”。 虽然事实确实是如此&a…

30个接口自动化测试面试题,赶紧收藏

1. 什么是接口自动化测试&#xff1f; 答&#xff1a;接口自动化测试是指使用自动化工具对接口进行测试&#xff0c;验证接口的正确性、稳定性和性能等方面的指标。2. 为什么要进行接口自动化测试&#xff1f; 答&#xff1a;接口自动化测试可以提高测试效率&#xff0c;减少人…

新能源行业如何进行数据防泄漏

客户情况 某新能源电池企业专业从事于新能源锂离子动力电池和储能电池的研发、生产和销售&#xff0c;具备电芯、模组、BMS及Pack的完整资源开发能力。公司致力于通过持续不断地改进电池技术&#xff0c;为全球锂离子动力和储能领域提供数字化精准高效的新能源解决方案。 该企…

Nautilus Chain 主网上线在即,一文盘点该生态即将上线的项目

Nautilus Chain 是行业内第一个并行化&#xff0c;且运行速度最快 EVM Rollup 的L3扩容方案&#xff0c;作为首个模块化链&#xff0c;存储、计算、共识等都在不同的模块中&#xff0c;意味着其能够获得更高的可拓展性与扩容能力&#xff0c;并在Layer2的基础上进一步提升了网络…

SpringCloud Alibaba-Sentinel

SpringCloud Alibaba-Sentinel 1. Sentinel核心库1.1 Sentinel介绍1.2 Sentinel核心功能1.2.1 流量控制1.2.2 熔断降级 2 Sentinel 限流熔断降级2.1 SentinelResource定义资源2.1.1 blockHandler/blockHandlerClass2.1.2 fallback/fallbackClass2.1.3 defaultFallback 2.2 Sent…

2、产品经理的工作内容

上一篇文章&#xff1a;1、产品经理的宏观定义_阿杰学编程的博客-CSDN博客 接下来这个章节里&#xff0c;我们有三个目标。 第一个通过案例&#xff0c;大家要了解一下产品经理的一个主要的工作内容。 第二个理解产品经理的一个重要性。 第三个我们要熟悉一下MVP的概念&…