【设计模式--行为型--备忘录模式】

news2024/9/29 9:27:53

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

    • 备忘录模式
      • 定义
      • 结构
      • 案例实现
        • 白箱备忘录模式
        • 黑箱备忘录模式
      • 优缺点
      • 使用场景

备忘录模式

定义

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

结构

  • 发起人角色(Originator):记录当前时刻的内部状态信息,提供创建备忘录和恢复备忘录数据的功能,实现其他业务功能,它可以访问备忘录里的所有信息。
  • 备忘录角色(Memento):负责存储发起人的内部状态,在需要的时候提供这些内部状态给发起人
  • 管理者角色(Caretaker):对备忘录进行管理,提供保存与获取备忘录的功能,但其不能对备忘录的内容进行访问与修改
    • 备忘录有两个等效接口:
    • 窄接口:管理者对象(和其他发起人对象之外的任何对象)看到的是备忘录的窄接口,这个窄接口只允许他把备忘录对象传递给其他的对象。
    • 宽接口:与管理者看到的窄接口相反,发起人对象可以看到一个宽接口,这个宽接口允许它读取所有的数据,以便根据这些数据恢复这个发起人对象内部状态。

案例实现

游戏保存,游戏角色有生命力,攻击力,防御力等数据,模拟玩家打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 = 100;
        this.def = 100;
    }

    // 保存角色状态
    public RoleStateMemento saveState() {
        return new RoleStateMemento(vit, atk, def);
    }

    // 恢复角色到初始状态
    public void recoverState(RoleStateMemento roleStatusMemento) {
        // 将备忘录中的数据赋值给当前对象
        this.vit = roleStatusMemento.getVit();
        this.atk = roleStatusMemento.getAtk();
        this.def = roleStatusMemento.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() {
    }

    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 {

    // 声明RoleStateMemento类型的变量
    private RoleStateMemento roleStatusMemento;

    public RoleStateMemento getRoleStatusMemento() {
        return roleStatusMemento;
    }

    public void setRoleStatusMemento(RoleStateMemento roleStatusMemento) {
        this.roleStatusMemento = roleStatusMemento;
    }
}
public class Test01 {
    public static void main(String[] args) {
        // 创建游戏角色对象
        GameRole gameRole = new GameRole();
        System.out.println("---boss关卡前---");
        gameRole.initState();
        gameRole.stateDisplay();
        // 将游戏角色内部状态进行备份
        // 创建管理者对象
        RoleStateCaretaker roleStateCaretaker = new RoleStateCaretaker();
        roleStateCaretaker.setRoleStatusMemento(gameRole.saveState());
        System.out.println("---挑战boss---");
        gameRole.fight();
        gameRole.stateDisplay();
        System.out.println("---生命为0,boss关挑战失败,回到boss关卡前---");
        gameRole.recoverState(roleStateCaretaker.getRoleStatusMemento());
        gameRole.stateDisplay();
    }
}

在这里插入图片描述

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

黑箱备忘录模式

备忘录角色对发起人提供一个宽接口,而为其他对象提供一个窄接口。在JAVA语言中,实现双重接口的办法就是将备忘录类设计成发起人类的内部成员类。将RoleStateMemento设为GameRole的内部类,从而将RoleStateMemento对象封装在GameRole里面;在外面提供一个标识接口Memento给RoleStateCaretaker及其他对象使用。这样GameRole类看到的是RoleStateMemento所有的接口,而RoleStateCaretaker及其他对象看到的仅仅是标识接口Memento所暴露出来的接口,从而维护了封装性。

在这里插入图片描述

/**
 * 备忘录接口 对外提供窄接口
 */
public interface Memento {
}
/**
 * 游戏角色类  发起人角色
 */
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 = 100;
        this.def = 100;
    }

    // 保存角色状态
    public Memento saveState() {
        return new RoleStateMemento(vit, atk, def);
    }


    // 恢复角色到初始状态
    public void recoverState(Memento memento) {
        RoleStateMemento roleStatusMemento = (RoleStateMemento) memento;
        // 将备忘录中的数据赋值给当前对象
        this.vit = roleStatusMemento.getVit();
        this.atk = roleStatusMemento.getAtk();
        this.def = roleStatusMemento.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() {
        }

        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 {

    // 声明RoleStateMemento类型的变量
    private Memento memento;

    public Memento getMemento() {
        return memento;
    }

    public void setMemento(Memento memento) {
        this.memento = memento;
    }
}
public class Test01 {
    public static void main(String[] args) {
        // 创建游戏角色对象
        GameRole gameRole = new GameRole();
        System.out.println("---boss关卡前---");
        gameRole.initState();
        gameRole.stateDisplay();
        // 将游戏角色内部状态进行备份
        // 创建管理者对象
        RoleStateCaretaker roleStateCaretaker = new RoleStateCaretaker();
        roleStateCaretaker.setMemento(gameRole.saveState());
        System.out.println("---挑战boss---");
        gameRole.fight();
        gameRole.stateDisplay();
        System.out.println("---生命为0,boss关挑战失败,回到boss关卡前---");
        gameRole.recoverState(roleStateCaretaker.getMemento());
        gameRole.stateDisplay();
    }
}

在这里插入图片描述

优缺点

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

使用场景

  • 需要保存与恢复数据的场景,例如游戏的存档功能。
  • 需要提供一个可回滚操作的场景。

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

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

相关文章

机器学习三个基本要素:优化算法

在确定了训练集 D、假设空间 ℱ 以及学习准则后,如何找到最优的模型𝑓(x,θ∗) 就成了一个最优化(Optimization)问题。机器学习的训练过程其实就是最优化问题的求解过程。 参数与超参数 在机器学习中,优化又可以分为参…

kafka offset sasl加密连接

kafka-tool(offset) 进行SCRAM连接,直接上图 填写jaas的认证(账密 引用包)

案例077:基于微信小程序的停车场管理系统设计与实现

文末获取源码 开发语言:Java 框架:SSM JDK版本:JDK1.8 数据库:mysql 5.7 开发软件:eclipse/myeclipse/idea Maven包:Maven3.5.4 小程序框架:uniapp 小程序开发软件:HBuilder X 小程序…

基于SSM的旅游管理系统论文

摘 要 互联网发展至今,无论是其理论还是技术都已经成熟,而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播,搭配信息管理工具可以很好地为人们提供服务。针对旅游信息管理混乱,出错率高,信息安全性差&am…

AR眼镜|AR智能眼镜开发|AR眼镜定制_5G联发科MTK解决方案

AR眼镜是一种提供身临其境的增强现实体验的设备,可以通过360度全方位展示产品的外观、结构和细节,让销售人员无需携带产品,用户也无需到店即可演示和体验产品。此外,AR眼镜还可以应用于远程协作,在任何时间和地点实现员…

pycharm手动安装ini插件

pycharm中新增pytest.ini文件时发现,文件的图标不是配置文件的图标 原因是没有安装ini插件 安装插件的方式有很多种,今天通过去官网下载插件,再安装的方式 第一步:去官网搜索,地址是:https://plugins.jet…

Java中的filter和Interceptor

一、filter(过滤器) 依赖于servlet容器。在实现上基于函数回调,可以对几乎所有请求进行过滤,但是缺点是一个过滤器实例只能在容器初始化时调用一次。使用过滤器的目的是用来做一些过滤操作,获取我们想要获取的数据&am…

MSVC编译 openssl windows 库

开发需要在windows下集成 openssl 库,参考官方指导完成了编译:openssl/NOTES-WINDOWS.md at master openssl/openssl 不过,最后还是走了直接下载的捷径。 1. 安装 ActivePerl 需要在 ActiveState 注册账户,之后彼会提供具体的…

开启创意之旅:免费、开源的噪波贴图(noise texture)生成网站——noisecreater.com详细介绍

在当今数字创意领域,噪波贴图(Noise Texture)是游戏渲染、游戏开发、美术设计以及影视制作等行业不可或缺的艺术素材之一。为了满足广大创作者的需求,noisecreater.com应运而生,成为一款免费、开源的噪波贴图生成工具。…

RocketMQ系统性学习-RocketMQ原理分析之消费者的接收消息流程

文章目录 消费者的接收消息流程 消费者的接收消息流程 还是先把消费者接收消息的流程图贴出来,再细说代码流程: 首先先从消费者的业务调用出发 // 创建消费者对象 DefaultMQPushConsumer consumer new DefaultMQPushConsumer("delay_group&quo…

使用极狐gitlab初始化导入本地项目

本地有项目的情况需要同步到极狐gitlab上 第一步: 在gitlab上新创建一个空项目 ⚠️⚠️⚠️这里需要注意红色圈住的地方一定不要选择,因为选择了这个后续会有不必要的麻烦 第二步 在本地项目中删除原来的.git文件(这一步如果是新项目可以忽略&#…

c# OpenCV 基本绘画(直线、椭圆、矩形、圆、多边形、文本)(四)

我们将在这里演示如何使用几何形状和文本注释图像。 Cv2.Line() 绘制直线 Cv2.Ellipse() 绘制椭圆Cv2.Rectangle() 绘制矩形Cv2.Circle() 绘制圆Cv2.FillPoly() 绘制多边形Cv2.PutText() 绘制文本 一、绘制直线 Cv2.Line(image, start_point, end_point, color, thickness) …

CleanMyMac X 4 for Mac(Mac优化清理工具)v4.14.6中文破解版

CleanMyMac X for Mac中文破解版只需两个简单步骤就可以把系统里那些乱七八糟的无用文件统统清理掉,节省宝贵的磁盘空间。cleanmymac x个人认为X代表界面上的最大升级,功能方面有更多增加,与最新macOS系统更加兼容,流畅地与系统性…

云计算:FusionCompute 通过 FreeNAS 添加SAN存储

目录 一、实验 1.环境准备 2.FusionCompute添加CNA 3.在存储中创建LUN资源映射给CNA节点 3.添加存储资源关联CNA主机节点 4.扫描存储资源 5.将存储设备添加为数据存储 二、问题 1.FusionCompute中存储如何分类 2.存储资源与存储设备有何区别 3.FusionCompute支持哪些…

JavaScript基础篇

目录 1.初始JavaScript 2.Js数据类型 2.1强制转换类型 1.转换为String类型 2.转换为Number类型 3.转换为 Boolean 4.转义符 2.2运算符 2.3分支结构 1.初始JavaScript <!-- 1. 文件引入 --> <!--<script src"./js/index.js"></script>-…

微软官宣放出一个「小模型」,仅2.7B参数,击败Llama2和Gemini Nano 2

就在前一阵谷歌深夜炸弹直接对标 GPT-4 放出 Gemini 之后&#xff0c;微软这两天也紧锣密鼓进行了一系列动作。尽管时间日趋圣诞假期&#xff0c;但是两家巨头硬碰硬的军备竞赛丝毫没有停止的意思。 就在昨日&#xff0c;微软官宣放出一个“小模型” Phi-2&#xff0c;这个 Ph…

在 VMware 虚拟机上安装黑苹果(Hackintosh):免费 macOS ISO 镜像下载及安装教程

在 VMware 虚拟机上安装黑苹果(Hackintosh)&#xff1a;免费 macOS ISO 镜像下载及安装教程 VMware 虚拟机解锁 macOS 安装选项使用 macOS iso 系统镜像安装使用 OpenCore 做引导程序安装 在 VMware 虚拟机上安装黑苹果(Hackintosh)&#xff1a;免费 macOS ISO 镜像下载及安装…

RPC(5):AJAX跨域请求处理

接上一篇RPC&#xff08;4&#xff09;&#xff1a;HttpClient实现RPC之POST请求进行修改。 1 修改客户端项目 1.1 修改maven文件 修改后配置文件如下&#xff1a; <dependencyManagement><dependencies><dependency><groupId>org.springframework.b…

【线性代数】两个向量组等价,其中一个向量组线性无关,另一个向量组也是线性无关吗?

一、问题 两个向量组等价,其中一个向量组线性无关,另一个向量组也是线性无关吗? 二、答案 不一定,当两个向量组中的向量个数也相同时,结论才成立.若向量个数不相同,结论不成立. 例如&#xff1a; 向量组一&#xff1a;(1,0),(0,1) 向量组二&#xff1a;(1,0),(0,1),(1,1) 两…

MyBatis ${}和#{}区别

sql防注入底层jdbc类型转换当简单类型参数$不防止Statment不转换value#防止preparedStatement转换任意 除模糊匹配外&#xff0c;杜绝使用${} MyBatis教程&#xff0c;大家可以借鉴 MyBatis 教程_w3cschoolMyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架。…