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

news2025/7/23 16:30:53

说明:本文介绍行为型设计模式之一的备忘录模式

定义

备忘录模式(Memento Pattern)又叫作快照模式(Snapshot Pattern)或令牌模式(Token Pattern)指在不破坏封装的前提下,捕获一个对象的内部状态,并在对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态,属于行为型设计模式。

(引自《设计模式就该这样学》P348,发现作者很喜欢使用”XX模式又叫作XX模式“这样的表述,笑)

编辑器

假设开发一款编辑器软件,如下,有载入文档、追加内容、清空内容功能;

(文档类,Doc)

/**
 * 文档类
 */
public class Doc {

    /**
     * 文档标题
     */
    private String title;

    /**
     * 文档内容
     */
    private StringBuffer body;

    public Doc(String title) {
        this.title = title;
        this.body = new StringBuffer();
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public StringBuffer getBody() {
        return body;
    }

    public void setBody(StringBuffer body) {
        this.body = body;
    }
}

(编辑器,Editor)

/**
 * 编辑器类
 */
public class Editor {

    private Doc doc;

    /**
     * 载入文档
     */
    public Editor(Doc doc) {
        System.out.println(">>>载入文档");
        this.doc = doc;
        show();
    }

    /**
     * 追加内容
     */
    public void append(String content){
        System.out.println(">>>追加内容");
        doc.getBody().append(content);
        show();
    }

    /**
     * 清空内容
     */
    public void clear() {
        System.out.println(">>>清空文档");
        doc.getBody().setLength(0);
        show();
    }

    /**
     * 保存内容
     */
    public void save() {
        System.out.println(">>>保存中");
        // todo
        System.out.println(">>>保存成功");
    }

    /**
     * 展示内容
     */
    public void show() {
        System.out.println(">>>展示内容");
        System.out.println("文档标题:" + doc.getTitle());
        System.out.println("文档内容:" + doc.getBody());
    }
}

(客户端使用,Client)

public class Client {
    public static void main(String[] args) {
        // 打开编辑器,开始写作
        Editor myDoc = new Editor(new Doc("《论程序员的自我修养》"));

        // 巴拉巴拉,写作中
        myDoc.append("\n第一章:程序员必备知识");
        myDoc.append("\n第二章:论艺术涵养对程序员编码的影响");

        // 看看,嗯,写得很好
        myDoc.show();
        // 保存
        myDoc.save();

        // 继续
        myDoc.append("\n第三章:论打游戏技术与程序员技术之间的关联");

        // 误操作。。。
        myDoc.clear();
    }
}

可见,如果误操作导致文档内容被清空,九分甚至十分的糟糕

在这里插入图片描述

针对以上功能,利用备忘录模式进行改造,如下:

(首先,创建一个历史记录对象,保存文档内容,History)

/**
 * 历史记录类
 */
public class History {

    private StringBuffer body;

    public History(StringBuffer body) {
        this.body = body;
    }

    public StringBuffer getBody() {
        return body;
    }
}

(其次,文档对象中,增加保存历史记录,恢复历史记录的方法)

/**
 * 文档类
 */
public class Doc {

    /**
     * 文档标题
     */
    private String title;

    /**
     * 文档内容
     */
    private StringBuffer body;

    public Doc(String title) {
        this.title = title;
        this.body = new StringBuffer();
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public StringBuffer getBody() {
        return body;
    }

    public void setBody(StringBuffer body) {
        this.body = body;
    }

    /**
     * 创建历史记录
     */
    public History createHistory() {
        return new History(new StringBuffer(body));
    }

    /**
     * 恢复历史记录
     */
    public void restoreHistory(History history) {
        body = history.getBody();
    }
}

注意创建历史记录方法里,使用了new StringBuffer(body),而不是直接传递body,这里涉及到深拷贝、浅拷贝的问题,大家可以试试看有什么区别。

(最后,改造编辑器类,增加撤销上一步操作的方法,并在每次修改操作后增加创建快照操作)

import java.util.List;

/**
 * 编辑器类
 */
public class Editor {

    /**
     * 文档对象
     */
    private Doc doc;

    /**
     * 历史记录集合
     */
    private List<History> historyList;

    /**
     * 历史记录版本号
     * 初始值为-1
     */
    private int historyVersion = -1;

    /**
     * 载入文档
     */
    public Editor(Doc doc) {
        System.out.println(">>>载入文档");
        this.doc = doc;
        historyList = new java.util.ArrayList<>();
        backup();
        show();
    }

    /**
     * 追加内容
     */
    public void append(String content){
        System.out.println(">>>追加内容");
        doc.getBody().append(content);
        backup();
        show();
    }

    /**
     * 清空内容
     */
    public void clear() {
        System.out.println(">>>清空文档");
        doc.getBody().setLength(0);
        backup();
        show();
    }

    /**
     * 保存内容
     */
    public void save() {
        System.out.println(">>>保存中");
        // todo
        System.out.println(">>>保存成功");
    }

    /**
     * 展示内容
     */
    public void show() {
        System.out.println(">>>展示内容");
        System.out.println("文档标题:" + doc.getTitle());
        System.out.println("文档内容:" + doc.getBody());
    }

    /**
     * 保存历史记录
     * 或者说创建快照
     */
    private void backup() {
        historyList.add(doc.createHistory());
        historyVersion++;
    }

    /**
     * 撤回上一步
     */
    public void undo() {
        System.out.println(">>>撤销操作");
        if (historyVersion == 0) {
            return;
        }
        historyVersion--;
        History history = historyList.get(historyVersion);
        doc.restoreHistory(history);
        show();
    }
}

(客户端使用,Client)

public class Client {
    public static void main(String[] args) {
        // 打开编辑器,开始写作
        Editor myDoc = new Editor(new Doc("《论程序员的自我修养》"));

        // 巴拉巴拉,写作中
        myDoc.append("\n第一章:程序员必备知识");
        myDoc.append("\n第二章:论艺术涵养对程序员编码的影响");

        // 看看,嗯,写得很好
        myDoc.show();
        // 保存
        myDoc.save();

        // 继续
        myDoc.append("\n第三章:论打游戏技术与程序员技术之间的关联");

        // 误操作。。。
        myDoc.clear();

        // 撤回上一步
        myDoc.undo();
    }
}

可见撤销操作成功恢复内容

在这里插入图片描述

多次撤销,可实现逐步回退操作

        // 撤回上一步
        myDoc.undo();
        myDoc.undo();
        myDoc.undo();
        myDoc.undo();

如下,非常nice。如果需要开发往后撤退的功能,也完全可以。

在这里插入图片描述

使用场景

在《设计模式就该这样学》(P365)这本书中,提到状态模式适用于以下场景:

(1)需要保存历史快照的场景。

(2)希望在对象之外保存状态,且除了自己,其他类对象无法访问状态保存的具体内容。

我觉得如果项目中,需要保存历史记录的场景,可以考虑使用备忘录模式进行改造。

总结

本文介绍了行为型设计模式中的状态模式,参考《设计模式就该这样学》、《秒懂设计模式》两书,编辑器场景是《秒懂设计模式》中的举例。

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

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

相关文章

docker离线镜像下载

背景介绍 在某些网络受限的环境中&#xff0c;直接从Docker Hub或其他在线仓库拉取镜像可能会遇到困难。为了在这种情况下也能顺利使用Docker镜像&#xff0c;我们可以提前下载好所需的镜像&#xff0c;并通过离线方式分发和使用。 当前镜像有&#xff1a;python-3.8-slim.ta…

Vert.x学习笔记-Verticle原理解析

Vert.x学习笔记 一、设计理念&#xff1a;事件驱动的组件化模型二、生命周期管理三、部署方式与策略四、通信机制&#xff1a;事件总线&#xff08;Event Bus&#xff09;五、底层实现原理六、典型应用场景七、Verticle与EventLoop的关系1、核心关系&#xff1a;一对一绑定与线…

jQuery和CSS3卡片列表布局特效

这是一款jQuery和CSS3卡片列表布局特效。该卡片布局使用owl.carousel.js来制作轮播效果&#xff0c;使用简单的css代码来制作卡片布局&#xff0c;整体效果时尚大方。 预览 下载 使用方法 在页面最后引入jquery和owl.carousel.js相关文件。 <link rel"stylesheet&qu…

不连网也能跑大模型?

一、这是个什么 App&#xff1f; 你有没有想过&#xff0c;不用连网&#xff0c;你的手机也能像 ChatGPT 那样生成文字、识别图片、甚至回答复杂问题&#xff1f;Google 最近悄悄发布了一个实验性 Android 应用——AI Edge Gallery&#xff0c;就是为此而生的。 这个应用不在…

强化学习鱼书(10)——更多深度强化学习的算法

&#xff1a;是否使用环境模型&#xff08;状态迁移函数P(s’|s,a)和奖 励函数r(s&#xff0c;a&#xff0c;V)&#xff09;。不使用环境模型的方法叫作无模型&#xff08;model-free&#xff09;的方法&#xff0c;使用环境模型的方法叫作有模型&#xff08;model-based&#…

K8S上使用helm部署 Prometheus + Grafana

一、使用 Helm 安装 Prometheus 1. 配置源 地址&#xff1a;prometheus 27.19.0 prometheus/prometheus-community # 添加repo $ helm repo add prometheus-community https://prometheus-community.github.io/helm-charts "prometheus-community" has been added…

Java面试八股--07-项目篇

致谢:2025年 Java 面试八股文(20w字)_java面试八股文-CSDN博客 目录 1、介绍一下最近做的项目 1.1 项目背景: 1.2 项目功能 1.3 技术栈 1.4自己负责的功能模块 1.5项目介绍参考: 1.6整体业务介绍: 1.8后台管理系统功能: 1.8.1后台主页: 1.8.2 商品模块: 1.8…

MCP架构全解析:从核心原理到企业级实践

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐&#xff1a;「storms…

从0到1认识EFK

一、ES集群部署 操作系统Ubuntu22.04LTS/主机名IP地址主机配置elk9110.0.0.91/244Core8GB100GB磁盘elk9210.0.0.92/244Core8GB100GB磁盘elk9310.0.0.93/244Core8GB100GB磁盘 1. 什么是ElasticStack? # 官网 https://www.elastic.co/ ElasticStack早期名称为elk。 elk分别…

定制开发开源AI智能名片驱动下的海报工厂S2B2C商城小程序运营策略——基于社群口碑传播与子市场细分的实证研究

摘要 本文聚焦“定制开发开源AI智能名片S2B2C商城小程序”技术与海报工厂业务的融合实践&#xff0c;探讨其如何通过风格化海报矩阵的精细化开发、AI技术驱动的用户体验升级&#xff0c;以及S2B2C模式下的社群裂变机制&#xff0c;实现“工具功能-社交传播-商业变现”的生态…

【Unity开发】控制手机移动端的震动

&#x1f43e; 个人主页 &#x1f43e; 阿松爱睡觉&#xff0c;横竖醒不来 &#x1f3c5;你可以不屠龙&#xff0c;但不能不磨剑&#x1f5e1; 目录 一、前言二、Unity的Handheld.Vibrate()三、调用Android原生代码四、NiceVibrations插件五、DeviceVibration插件六、控制游戏手…

Cesium快速入门到精通系列教程二:添加地形与添加自定义地形、相机控制

一、添加地形与添加自定义地形 在 Cesium 1.93 中添加地形可以通过配置terrainProvider实现。Cesium 支持多种地形数据源&#xff0c;包括 Cesium Ion 提供的全球地形、自定义地形服务以及开源地形数据。下面介绍几种常见的添加地形的方法&#xff1a; 使用 Cesium Ion 全球地…

python学习打卡day43

DAY 43 复习日 作业&#xff1a; kaggle找到一个图像数据集&#xff0c;用cnn网络进行训练并且用grad-cam做可视化 浙大疏锦行 数据集使用猫狗数据集&#xff0c;训练集中包含猫图像4000张、狗图像4005张。测试集包含猫图像1012张&#xff0c;狗图像1013张。以下是数据集的下…

Microsoft Word使用技巧分享(本科毕业论文版)

小铃铛最近终于完成了毕业答辩后空闲下来了&#xff0c;但是由于学校没有给出准确地参考模板&#xff0c;相信诸位朋友们也在调整排版时感到头疼&#xff0c;接下来小铃铛就自己使用到的一些排版技巧分享给大家。 注&#xff1a;以下某些设置是根据哈尔滨工业大学&#xff08;威…

windows安装多个版本composer

一、需求场景 公司存在多个项目&#xff0c;有的项目比较老&#xff0c;需要composer 1.X版本才能使用 新的项目又需要composer 2.X版本才能使用 所以需要同时安装多个版本的composer二、下载多个版本composer #composer官网 https://getcomposer.org/download/三、放到指定目…

【办公类-22-05】20250601Python模拟点击鼠标上传CSDN12篇

、 背景需求: 每周为了获取流量券,每天上传2篇,获得1500流量券,每周共上传12篇,才能获得3000和500的券。之前我用UIBOT模拟上传12篇。 【办公类-22-04】20240418 UIBOT模拟上传每天两篇,获取流量券,并删除内容_csdn 每日任务流量券-CSDN博客文章浏览阅读863次,点赞18…

贪心算法应用:边着色问题详解

贪心算法应用&#xff1a;边着色问题详解 贪心算法是一种在每一步选择中都采取当前状态下最优的选择&#xff0c;从而希望导致结果是全局最优的算法策略。边着色问题是图论中的一个经典问题&#xff0c;贪心算法可以有效地解决它。下面我将从基础概念到具体实现&#xff0c;全…

ck-editor5的研究 (2):对 CKEditor5 进行设计,并封装成一个可用的 vue 组件

前言 在上一篇文章中—— ck-editor5的研究&#xff08;1&#xff09;&#xff1a;快速把 CKEditor5 集成到 nuxt 中 &#xff0c;我仅仅是把 ckeditor5 引入到了 nuxt 中&#xff0c;功能还不算通用。 这一篇内容将会对其进行设计&#xff0c;并封装成可复用的 vue 组件&…

Java-redis实现限时在线秒杀功能

1.使用redisson pom文件添加redisson <!--redisson--><dependency><groupId>org.redisson</groupId><artifactId>redisson-spring-boot-starter</artifactId><version>3.23.4</version></dependency> 2.mysql数据库表设…

simulink mask、sfunction和tlc的联动、接口

这里全部是讲的level2 sfunction&#xff08;用m语言编写&#xff09;&#xff0c;基于matlab 2020a。 1.mask的参数操作 1&#xff09;mask通过set_param和get_param这2个函数接口对mask里面定义的Parameters&Dialog的参数的大部分属性进行读写&#xff0c;一般是Value值…