设计模式-备忘录模式

# 备忘录模式

1.简介

备忘录模式是一种行为设计模式,允许在不暴露对象实现细节的情况下保存和恢复对象之前的状态。

简单的来说就是在某一时刻把当前状态记录下来,以后可以选择性的再恢复到某次保存的状态。

例如游戏中的存档、编辑器中的 crtl+z、浏览器中的后退、数据库的事务管理等等。

2.UML图

  • **原发器(Originator):**类可以生成自身状态的快找,也可以在需要时通过快照恢复自身状态。
  • **备忘录(Memento):**是原发器状态快照的值对象。通常做法是将备忘录设为不可变得,并通过构造函数一次性传递数据。
  • **负责人(Caretaker):**仅知道 何时、为何 捕捉原发器的状态,以及何时恢复状态。 负责人通过保存备忘录栈来记录原发器的历史状态,当原发器需要回溯历史状态时,负责人将从栈中获取最顶部的备忘录,并将其传给原发器的恢复方法。
  • 在该实现方法中,备忘录类将被嵌套在原发器中。这样原发器就可以访问备忘录的成员变量和方法,即使这些方法被声明为私有。另一方面,负责人对于备忘录的成员变量和方法的访问权限非常有限;它们只能在栈中保存备忘录,而不能修改其状态。

3.代码示例

我们来写一个游戏存档的例子。

首先定义Originator(游戏类):

我们需要保存和读取游戏进度,它内部提供了两个方法,一个是对外提供备忘录(封装了要恢复的内部状态)。另一个是从外接受备忘录,用来恢复内部状态。

java 复制代码
package com.gs.designmodel.memento;

/**
 * @author: Gaos
 * @Date: 2023-08-22 18:35
 **/
public class GameOriginator {

    private int currentScore;

    /**
     * 将需要保存的状态封装在Memento里对外提供
     * @return
     */
    public GameProgressMemento saveProcess() {
        return new GameProgressMemento(currentScore);
    }

    /**
     * 通过从外部接收的Memento恢复状态
     * @param memento
     */
    public void restoreProcess(GameProgressMemento memento) {
        currentScore = memento.getScore();
    }

    public void playGame() {
        System.out.println("Game Begin---");
        System.out.println("当前分数为: " + currentScore);
        System.out.println("击杀小兵得一分");
        currentScore++;
        System.out.println("总分为: " + currentScore);
    }

    public void  exitGame() {
        System.out.println("退出游戏");
        currentScore = 0;
        System.out.println("Game End-----");
    }
}

构建Memento(备忘录):

就只是一个简单的实体类,不包含业务逻辑只包含数据状态,结构由要保存的状态类决定,我们这里只保存一个游戏分数。

java 复制代码
package com.gs.designmodel.memento;

/**
 * @author: Gaos
 * @Date: 2023-08-22 18:38
 **/
public class GameProgressMemento {

    private int score;

    public GameProgressMemento(int score) {
        this.score = score;
    }

    public int getScore() {
        return score;
    }
}

构建CareTaker(负责人):

CareTaker相对于 Originator来说是一个外部组件,它帮助 Originator保存了状态。

java 复制代码
package com.gs.designmodel.memento;

import java.util.ArrayList;
import java.util.List;

/**
 * @author: Gaos
 * @Date: 2023-08-22 18:38
 **/
public class GameCareTaker {

    private List<GameProgressMemento> mementos  = new ArrayList<>();

    /**
     * 保存状态
     * @param memento
     */
    public void saveMemento(GameProgressMemento memento) {
        this.mementos.add(memento);
    }

    /**
     * 恢复状态
     * @param index
     * @return
     */
    public GameProgressMemento getMemento(int index) {
        return this.mementos.get(index);
    }
}

测试类:

java 复制代码
package com.gs.designmodel.memento;

/**
 * @author: Gaos
 * @Date: 2023-08-22 18:46
 **/
public class Test {
    public static void main(String[] args) {
        GameOriginator originator = new GameOriginator();
        GameCareTaker careTaker = new GameCareTaker();
        // 客户端需要维护一个指针和备忘录的次序相对应
        int index = 0;
        // 玩游戏
        originator.playGame();
        // 保存进度
        careTaker.saveMemento(originator.saveProcess());
        // 退出游戏
        originator.exitGame();

        // 重新打开游戏恢复进度
        originator.restoreProcess(careTaker.getMemento(index));
        originator.playGame();
    }
}

结果:

sql 复制代码
Game Begin---
当前分数为: 0
击杀小兵得一分
总分为: 1
退出游戏
Game End-----
Game Begin---
当前分数为: 1
击杀小兵得一分
总分为: 2

4.总结

当你需要创建对象状态快照来恢复其之前的状态时,你可以使用备忘录模式。当对象的安全性被突破时你可以使用备忘录模式。(备忘录让对象自行负责创建其状态的快找,其他任何对象都不能读取快照,这保障了数据的安全性)

你可以使用 命令模式和备忘录模式来实现撤销,命令对目标对象执行各种不同的操作,备忘录用来保存一条命令执行前盖对象的状态。但是如果客户端太过于频繁的创建备忘录,程序将消耗大量内存,同时 负责人(CareTaker)必须根据原发器的生命周期,这样才能销毁弃用的备忘录。

参考文章:

refactoringguru.cn/design-patt...

blog.csdn.net/ShuSheng000...

希望这篇文章对大家有所帮助,您的赞和收藏是对我最大的支持和认可!

相关推荐
m0_748245172 分钟前
Web第一次作业
java
小码的头发丝、2 分钟前
Java进阶学习笔记|面向对象
java·笔记·学习
m0_548514776 分钟前
前端Pako.js 压缩解压库 与 Java 的 zlib 压缩与解压 的互通实现
java·前端·javascript
坊钰34 分钟前
【Java 数据结构】移除链表元素
java·开发语言·数据结构·学习·链表
chenziang139 分钟前
leetcode hot100 LRU缓存
java·开发语言
会说法语的猪1 小时前
springboot实现图片上传、下载功能
java·spring boot·后端
码农老起1 小时前
IntelliJ IDEA 基本使用教程及Spring Boot项目搭建实战
java·ide·intellij-idea
m0_748239831 小时前
基于web的音乐网站(Java+SpringBoot+Mysql)
java·前端·spring boot
时雨h1 小时前
RuoYi-ue前端分离版部署流程
java·开发语言·前端