设计模式备忘录+命令模式实现Word撤销恢复操作

文章目录

  • 前言
  • 思路
  • 代码实现
  • uml类图
  • 总结

前言

最近学习设计模式行为型的模式,学到了备忘录模式提到这个模式可以记录一个对象的状态属性值,用于下次复用,于是便想到了我们在Windows系统上使用的撤销操作,于是便想着使用这个模式进行一次模仿复现

思路

以下是按照备忘录和命令模式结合的思路描述:

  1. 首先,我们有一个文档类 Document,其中包含一个文本属性。文档类提供了设置和获取文本的方法。

  2. 我们引入备忘录类 Memento,用于保存文档对象的状态。

  3. 文档类还实现了创建备忘录和恢复备忘录的方法。创建备忘录时,文档对象会将当前的文本状态传递给备忘录对象进行保存。恢复备忘录时,文档对象会从备忘录对象中获取之前保存的文本状态并恢复。

  4. 为了实现可逆操作和撤销功能,我们引入了命令接口 Command,该接口定义了执行方法 execute() 和撤销方法 undo()

  5. 具体命令类 InsertTextCommand 是一个插入文本操作的具体实现。在执行命令时,该命令对象会调用文档对象的插入文本方法,并将执行前的文本状态保存到备忘录对象中。在撤销命令时,该命令对象会使用备忘录对象恢复文档的文本状态。

  6. 历史记录类 History 充当调用者的角色,用于记录执行的命令。它内部使用一个列表来保存命令对象。每次执行命令时,该命令对象被添加到列表中保存;每次撤销命令时,列表的最后一个命令对象被取出并执行其撤销操作。

  7. 在主程序中,我们实例化了文档对象、备忘录对象和历史记录对象。

  8. 执行插入文本命令1,创建插入文本命令对象并将其添加到历史记录对象的列表中。该命令对象会执行插入文本操作,并将执行前的文本状态保存到备忘录对象中。

  9. 执行插入文本命令2,同样创建插入文本命令对象并将其添加到历史记录对象的列表中。该命令对象也会执行插入文本操作,并将执行前的文本状态保存到备忘录对象中。

  10. 执行撤销命令,历史记录对象的列表中取出最后一个命令对象(即插入文本命令2),并执行其撤销操作。命令对象会从备忘录对象中获取之前保存的文本状态,恢复文档的内容。

  11. 输出文档的内容,即输出 “Hello”。

  12. 再次执行撤销命令,历史记录对象的列表中取出插入文本命令1,并执行其撤销操作。文档的内容变为空字符串。

  13. 输出文档的内容,即输出空字符串。

  14. 尝试再次执行撤销命令,由于历史记录中已没有可撤销的命令,不会执行任何操作。

  15. 最后,输出文档的内容,依然输出空字符串。

代码实现

实现类似于 Word 文档中的撤销和恢复操作,可以采用备忘录模式配合命令模式的方式。

  1. 备忘录类(Memento):备忘录类负责存储文档的状态。它可以保存文档的内容、样式、光标位置等信息。
class Memento {
    private String content;
    private String style;
    private int cursorPosition;

    // 构造函数和访问方法
}
  1. 命令接口(Command):命令接口定义执行和撤销操作的方法。
interface Command {
    void execute();
    void undo();
}
  1. 具体命令类(具体的操作):实现命令接口,执行和撤销文档的具体操作。例如,插入文本、修改样式、移动光标等。
class InsertTextCommand implements Command {
    private Document document;
    private Memento prevState;
    private String newText;

    public InsertTextCommand(Document document, String newText) {
        this.document = document;
        this.newText = newText;
    }

    public void execute() {
        prevState = document.createMemento();
        document.setText(newText);
    }

    public void undo() {
        document.restore(prevState);
    }
}
  1. 文档类(Originator):文档类维护文档的状态,并提供创建备忘录、恢复状态和执行操作的方法。
class Document {
    private String text;

    public void setText(String text) {
        this.text = text;
    }

    public String getText() {
        return text;
    }

    public Memento createMemento() {
        return new Memento(text);
    }

    public void restore(Memento memento) {
        text = memento.getState();
    }
}
  1. 历史记录类(Caretaker):历史记录类负责存储备忘录对象,并管理执行和撤销操作的命令。
class History {
    private Stack<Command> commandStack;

    public History() {
        commandStack = new Stack<>();
    }

    public void executeCommand(Command command) {
        command.execute();
        commandStack.push(command);
    }

    public void undo() {
        if (!commandStack.isEmpty()) {
            Command command = commandStack.pop();
            command.undo();
        }
    }
}

使用以上设计的示例代码如下:

    public static void main(String[] args) {
        // 创建文档对象和历史记录对象
        Document document = new Document();
        History history = new History();

        // 执行命令:插入文本
        Command insertCommand1 = new InsertTextCommand(document, "Hello");
        history.executeCommand(insertCommand1);

        // 执行命令:插入文本
        Command insertCommand2 = new InsertTextCommand(document, " World!");
        history.executeCommand(insertCommand2);
        

        // 输出文档内容
        System.out.println(document.getText());  // 输出:World!

        // 执行命令:撤销上一个命令
        history.undo();

        // 输出文档内容
        System.out.println(document.getText());  // 输出:hello

        // 执行命令:撤销上一个命令(没有可撤销的命令)
        history.undo();  // 不执行任何操作

        // 输出文档内容
        System.out.println(document.getText());  // 输出:""
    }

在这里插入图片描述

通过使用备忘录模式和命令模式,我们可以记录文档状态的变化,并在需要时进行撤销和恢复操作。每次执行操作时,都会创建对应的命令对象,并将其添加到历史记录中,以支持撤销和重做操作。

uml类图

在这里插入图片描述

总结

这个功能的实现使用了备忘录模式和命令模式两种设计模式。

备忘录模式用于保存文档对象的状态,并提供了恢复状态的功能。它将文档对象的状态封装在备忘录对象中,以便在需要时可以对其进行保存并恢复。这样,可以在不破坏文档对象封装性的情况下,实现文档对象的状态管理和回滚功能。

命令模式用于执行和撤销操作。通过将每个操作封装在一个命令对象中,并提供统一的执行和撤销方法,可以实现对操作的统一管理和控制。这样,可以方便地扩展和组合不同的操作,同时也解耦了调用者和接收者。

使用设计模式的好处包括:

  1. 提高代码的可维护性和可扩展性:设计模式使代码结构更清晰、更易于理解和维护。模式中定义了明确的角色和关系,使代码具有良好的组织结构和可扩展性。

  2. 复用性增加:设计模式通过提供通用的解决方案,使得代码可以在不同场景下被重复使用。这避免了重复编写相似的代码,提高了开发效率。

  3. 降低耦合度:设计模式通过明确角色和关系,将系统中各组件之间的依赖关系降到最低。这样,当需求变化或者需要修改某一个组件时,对其他组件的影响最小,易于维护和扩展。

  4. 提高代码的可测试性:设计模式将逻辑分离开来,使得每个模块可以独立地进行测试,便于编写单元测试和集成测试。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

记一种不错的缓存设计思路

之前与同事讨论接口性能问题时听他介绍了一种缓存设计思路&#xff0c;觉得不错&#xff0c;做个记录供以后参考。 场景 假设有个以下格式的接口&#xff1a; GET /api?keys{key1,key2,key3,...}&types{1,2,3,...} 其中 keys 是业务主键列表&#xff0c;types 是想要取到的…

[FPGA IP系列] BRAM IP参数配置与使用示例

FPGA开发中使用频率非常高的两个IP就是FIFO和BRAM&#xff0c;上一篇文章中已经详细介绍了Vivado FIFO IP&#xff0c;今天我们来聊一聊BRAM IP。 本文将详细介绍Vivado中BRAM IP的配置方式和使用技巧。 一、BRAM IP核的配置 1、打开BRAM IP核 在Vivado的IP Catalog中找到B…

玩转科技|了解AI平台桌面客户端—ChatBox

目录 前言 特性 ​编辑 为什么需要 ChatBox&#xff1f; ChatGPT Plus 平替&#xff1f; 下载 支持系统 功能图 使用教程 ​感受 展示 前言 今天小编又来了&#xff0c;推荐给大家一款开源的OpenAI API桌面客户端ChatBox&#xff0c;它支持 Windows、Mac 和 Linux。…

C# task多线程创建,暂停,继续,结束使用

1、多线程任务创建 private void button1_Click(object sender, EventArgs e) //创建线程{CancellationToken cancellationToken tokensource.Token;Task.Run(() > //模拟耗时任务{for (int i 0; i < 100; i){if (cancellationToken.IsCancellationRequested){return;…

900ES1-0100 honeywell 可减少视觉引导应用的整体开发时间

900ES1-0100 honeywell 可减少视觉引导应用的整体开发时间 CV2视觉系统配有高柔性电缆(以太网或USB)。通过将高柔性电缆作为所有CV2视觉系统的标准配置&#xff0c;Epson CV2摄像机可以安装在机器人臂(移动)或固定装置(固定)上。基于向导的校准使机器人到视觉系统的校准变得轻…

HAproxy(四十七)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 前言 一、概述 1.1 简介 1.2 核心功能 1.3 关键特性 1.4 应用场景 二、安装 1.内核配置 2.编译安装 ​3. 建立配置文件 4. 添加为系统服务 5. 添加3和5运行级别下自启动…

c#在MVC Api(.net framework)当中使用Swagger,以及Demo下载

主要的步骤就是创建项目&#xff0c;通过nuget 添加Swashbuckle包&#xff0c;然后在SwaggerConfig当中进行相关的配置。 具体的步骤&#xff0c;可以参考下面的链接&#xff1a; https://www.cnblogs.com/94pm/p/8046580.htmlhttps://blog.csdn.net/xiaouncle/article/detail…

无套路,财务数据分析-多组织损益表分析分享

在报表众多的财务数据分析中&#xff0c;损益表是老板们最关注的报表&#xff0c;特别是当有多组织时&#xff0c;损益表的分析就变得更加重要了。以前受限于数据分析工具&#xff0c;做损益表分析时很难做到多维度灵活分析&#xff0c;但随着BI数据可视化工具的发展&#xff0…

【Unity3D赛车游戏】【七】如何在Unity中为汽车添加自动变速箱自动换挡?

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;Uni…

数据分析作业四-基于用户及物品数据进行内容推荐

## 导入支持库 import pandas as pd import matplotlib.pyplot as plt import sklearn.metrics as metrics import numpy as np from sklearn.neighbors import NearestNeighbors from scipy.spatial.distance import correlation from sklearn.metrics.pairwise import pairwi…

死信队列理解与使用

一、简介 在rabbitMQ中常用的交换机有三种&#xff0c;直连交换机、广播交换机、主题交换机&#xff1b; 直连交换机中队列与交换机需要约定好routingKey去进行绑定&#xff1b; 广播交换机并不需要routingKey绑定,只需队列与交换机绑定即可&#xff1b; 主题交换机最大的特…

monorepo更新组件报错,提示“无法加载文件 C:\Program Files\nodejs\pnpm.ps1,因为在此系统上禁止运行脚本”

解决方法&#xff1a; 第一步&#xff1a;管理员身份运行 window.powershell&#xff0c; win x打开powerShell命令框&#xff0c;进入到对应项目路径。 第二步&#xff1a;执行&#xff1a;get-ExecutionPolicy&#xff0c;显示Restricted&#xff0c;表示状态是禁止的; 第…

TCP协议的重点知识点

TCP协议的重点知识点 TCP(传输控制协议)是一种面向连接、可靠的数据传输协议,工作在传输层,提供可靠的字节流服务。它是互联网协议栈中最重要、最复杂的协议之一,也是面试中常被问到的知识点。本文将详细介绍TCP协议的各个重要概念。 TCP基本特性 TCP主要具有以下基本特性: …

如何用 QGIS 下载高清天地图影像机,同时解决下载质量差的问题!

使用 QGIS 我们可以获得下面这种图像,既有大范围,又有更高的细节(地图级别),基本上把整个苏州市中心城区的建筑物都囊括进去了。 还可以下载大范围、高清晰度的各种在线卫星底图服务的影像,比如大面积的哨兵2影像,但国外的服务器一般都很烂,不可能是电信、移动的问题,…

python爬虫12:实战4

python爬虫12&#xff1a;实战4 前言 ​ python实现网络爬虫非常简单&#xff0c;只需要掌握一定的基础知识和一定的库使用技巧即可。本系列目标旨在梳理相关知识点&#xff0c;方便以后复习。 申明 ​ 本系列所涉及的代码仅用于个人研究与讨论&#xff0c;并不会对网站产生不好…

QGIS 如何添加天地图

相信很多小伙伴在 QGIS 里面添加天地图的时候一定感觉很困惑,按照官网的操作申请 Key 之后,添加相对应的服务地址之后看不到地图或者地图不正常显示,今天我们就来解决这个问题 以下所有操作基于 QGIS 3.22 版本 申请 Key 1. 添加天地图的第一步需要申请 Key,首先要注册天…

Git基础教程-常用命令整理:学会Git使用方法和错误解决

目录 一、了解Git的基本概念 二、Git的安装和配置 Git的安装 Git的配置 用户信息 文本编辑器 差异分析工具 查看配置信息 三、Git的基本操作 基本原理 基本操作命令 基本操作示例 场景一&#xff1a;创建新仓库 场景二&#xff1a;拉取并编辑远程仓库 四、常见问…

MySQL之事务与引擎

目录 一、事物 1、事务的概念 2、事务的ACID特点 3、事务之间的相互影响 4、Mysql及事务隔离级别(四种) 5、演示 1、查询会话事务隔离级别 2、查询会话事务隔离级别 3、设置全局事务隔离级别 4、设置会话事务隔离级别 6、事务控制语句 7、演示 1、测试提交事务 2、测试事…

countDown+react+hook

道阻且长&#xff0c;行而不辍&#xff0c;未来可期 知识点一&#xff1a; new Date().getTime()可以得到得到1970年01月1日0点零分以来的毫秒数。单位是毫秒 new Date().getTime()/1000获取秒数1分钟60秒&#xff0c;1小时60分钟1hour:60*60>单位是秒 60*60*1000>单位…

Java的成员类可以被private修饰

说明 Java的成员类可以被private修饰&#xff0c;但外部类、局部类不能被private修饰。 示例 成员类用private修饰—允许 下面代码中的成员类Class2 被private修饰&#xff0c;是允许的&#xff1a; package com.thb;public class Parent {public class Class1 { }private…