备忘录设计模式(Memento Pattern)[论点:概念、组成角色、示例代码、框架中的运用、适用场景]

news2024/9/22 2:41:45

文章目录

  • 概念
  • 组成角色
  • 示例代码
  • 框架中的运用
  • 适用场景

概念

备忘录模式(Memento Pattern)是一种行为型设计模式,主要用于保存对象的内部状态,以便在需要时恢复到先前的状态。这种模式有助于实现撤销、恢复或回滚操作,同时保持对象封装性。

组成角色

  1. 发起人(Originator):负责创建一个备忘录,用于存储当前对象的内部状态,并在需要时恢复到先前的状态。
  2. 备忘录(Memento):存储发起人对象的内部状态。备忘录应该只能被发起人访问和修改。
  3. 管理者(Caretaker):负责存储备忘录。管理者不应修改或直接访问备忘录的内容。

示例代码

        在此示例中,BankAccount(Originator)类表示一个简单的银行账户,支持存款和取款操作。每次执行操作时,我们都将当前余额保存到一个BankAccountMemento(Memento)对象中,并将其添加到BankAccountCaretaker对象的列表中。当需要执行撤销操作时,我们可以通过BankAccountCaretaker对象获取相应的BankAccountMemento对象,并将BankAccount的余额恢复到先前的状态。

package design.pattern.Memento;

import java.util.Stack;

// 定义一个可撤销操作的接口
interface UndoableOperation {
    void deposit(double amount);
    void withdraw(double amount);
    void undo();
    void redo();
    double getBalance();
}

// Memento 类,存储银行账户的余额
class BankAccountMemento {
    private double balance;

    public BankAccountMemento(double balance) {
        this.balance = balance;
    }

    public double getBalance() {
        return balance;
    }
}

// Originator 类,实现了 UndoableOperation 接口
class BankAccount implements UndoableOperation {
    private double balance;

    // 用来存储撤销操作的栈
    private Stack<BankAccountMemento> undoStack = new Stack<>();
    // 用来存储重做操作的栈
    private Stack<BankAccountMemento> redoStack = new Stack<>();

    public BankAccount(double balance) {
        this.balance = balance;
        undoStack.push(saveToMemento());
    }

    @Override
    public void deposit(double amount) {
        redoStack.clear();
        balance += amount;
        undoStack.push(saveToMemento());
    }

    @Override
    public void withdraw(double amount) {
        redoStack.clear();
        balance -= amount;
        undoStack.push(saveToMemento());
    }

    @Override
    public double getBalance() {
        return balance;
    }

    private BankAccountMemento saveToMemento() {
        return new BankAccountMemento(balance);
    }

    private void restoreFromMemento(BankAccountMemento memento) {
        balance = memento.getBalance();
    }

    @Override
    public void undo() {
        if (!undoStack.isEmpty()) {
            redoStack.push(undoStack.pop());
            if (!undoStack.isEmpty()) {
                restoreFromMemento(undoStack.peek());
            }
        }
    }

    @Override
    public void redo() {
        if (!redoStack.isEmpty()) {
            BankAccountMemento memento = redoStack.pop();
            restoreFromMemento(memento);
            undoStack.push(memento);
        }
    }
}


//扮演Caretaker的角色
public class MementoPatternDemo {
    public static void main(String[] args) {
        // 创建一个实现了 UndoableOperation 接口的 BankAccount 对象,开始操作时金额为1000
        UndoableOperation bankAccount = new BankAccount(1000.0);

        // 执行存款操作
        bankAccount.deposit(500);
        System.out.println("Current balance: " + ((BankAccount) bankAccount).getBalance());

        // 执行取款操作
        bankAccount.withdraw(200);
        System.out.println("Current balance: " + ((BankAccount) bankAccount).getBalance());

        // 执行撤销操作
        bankAccount.undo();
        System.out.println("Undo last change: " + ((BankAccount) bankAccount).getBalance());

        // 再次执行撤销操作
        bankAccount.undo();
        System.out.println("Undo two changes: " + ((BankAccount) bankAccount).getBalance());

        // 执行重做操作
        bankAccount.redo();
        System.out.println("Redo last change: " + ((BankAccount) bankAccount).getBalance());

        //*             (存500)      (取200)       (撤销一次操作)       (撤销两次操作)       (执行重做操作)         *//
        //*金额变化:1000 ------> 1500 ------> 1300 ------------> 1500 ------------> 1000 ------------>  1500   *//
    }
}


框架中的运用

Swing库中的javax.swing.undo包,它提供了一种在Swing应用程序中实现撤销和重做功能的通用框架。

首先,我们需要创建一个简单的Swing应用程序,包含一个JTextArea组件,并添加撤销和重做按钮。

import javax.swing.*;
import javax.swing.undo.CannotRedoException;
import javax.swing.undo.CannotUndoException;
import javax.swing.undo.UndoManager;
import java.awt.*;

public class SwingMementoDemo {
    public static void main(String[] args) {
      	//在这个示例中,我们使用了Swing库中的UndoManager类,它实际上是一个Caretaker角色的实现。UndoManager类维护一个存储UndoableEdit对象(表示可撤销的编辑操作)的栈。UndoableEdit是一个接口,表示一个可撤销和重做的编辑操作,它的实现类充当了Memento角色。
        JFrame frame = new JFrame("Swing Memento Demo");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(400, 300);

        JTextArea textArea = new JTextArea();
        JScrollPane scrollPane = new JScrollPane(textArea);
        frame.add(scrollPane, BorderLayout.CENTER);

        // Set up UndoManager
        UndoManager undoManager = new UndoManager();
        textArea.getDocument().addUndoableEditListener(undoManager);

        // Set up buttons
        JPanel buttonPanel = new JPanel();
        JButton undoButton = new JButton("Undo");
        JButton redoButton = new JButton("Redo");
        buttonPanel.add(undoButton);
        buttonPanel.add(redoButton);

        frame.add(buttonPanel, BorderLayout.NORTH);

        // Set up button actions
        undoButton.addActionListener(e -> {
            try {
                if (undoManager.canUndo()) {
                    undoManager.undo();
                }
            } catch (CannotUndoException ex) {
                ex.printStackTrace();
            }
        });

        redoButton.addActionListener(e -> {
            try {
                if (undoManager.canRedo()) {
                    undoManager.redo();
                }
            } catch (CannotRedoException ex) {
                ex.printStackTrace();
            }
        });

        frame.setVisible(true);
    }
}

运行代码
可以在窗口中进行撤销和重做操作

在这里插入图片描述

适用场景

  1. 需要实现撤销(Undo)和重做(Redo)操作:在文本编辑器、图像编辑器或数据库事务中,用户可能希望撤销或重做先前的操作。使用备忘录模式,可以保存对象的状态,然后在需要时恢复到特定的状态。
  2. 需要备份和恢复状态:在某些情况下,可能需要在某个时间点创建对象状态的快照,并在将来的某个时刻将对象恢复到该状态。例如,在游戏中,玩家可能希望保存游戏进度并在以后继续游戏。
  3. 需要限制对象状态的直接访问:如果希望对外部对象隐藏某个对象的内部状态,可以使用备忘录模式。这样,外部对象无法直接访问或修改对象的内部状态,只能通过Originator提供的接口与其状态进行交互。
  4. 需要在不违反封装原则的前提下,暂存对象的内部状态:备忘录模式允许对象在不暴露其实现细节的情况下,保存和恢复其内部状态。这符合封装原则,有助于保持代码的整洁和易于维护。

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

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

相关文章

软件测试之基础概念学习篇(需求 + 测试用例 + 开发模型 + 测试模型 + BUG)

文章目录 1. 什么是软件测试2. 软件测试和软件开发的区别3. 软件测试和软件调试的区别4. 什么是需求1&#xff09;以需求为依据设计测试用例 5. 测试用例是什么6. 什么是 BUG&#xff08;软件错误&#xff09;7. 五个开发模型1&#xff09;瀑布模型2&#xff09;螺旋模型3&…

有哪些方法可以防止企业内部数据泄露?

企业内部数据安全威胁多数源于企业内部&#xff0c;企业内部存在许多安全威胁&#xff0c;主要包括: 1. 员工误操作或疏忽&#xff1a;这是导致内部安全事故的最主要原因之一。员工对安全程序缺乏认识或执行不严谨&#xff0c;可能会无意中泄露敏感数据或引发系统漏洞。 2. 未经…

Windows 服务监控工具

在任何企业中&#xff0c;Windows 服务都是面向业务的应用程序的核心组件。这些 Windows 服务的有效运行对于防止网络和应用程序停机至关重要。这使得 Windows 服务监视成为任何网络管理策略的关键部分。 Windows 服务监视使管理员能够确保关键 Windows 服务的可用性&#xff…

mysql的函数 --- MySQL总结(二)

mysql的函数 文章目录 mysql的函数日期函数字符串函数其他函数 日期函数 上面的所以的函数都可以使用select进行展现相应的数据 这里的使用current_date()时间戳&#xff0c;其他日期都可以相似的操作进行对于时间的操作 字符串函数 后面这里的操作查找name表之中的id的字符集&…

重构这件“小”事儿 | 得物技术

本文以一个Web项目的业务代码重构实践作为依据&#xff0c;来探讨项目业务代码重构过程中遇到的开发问题&#xff0c;以及重构过程中的一些注意点&#xff0c;希望可以给项目开发和服务开发维护重构提供一些通用的参考与思路。 这里不探讨大型项目的重构实践&#xff0c;毕竟一…

SHELL的脚本编写(2)

目录 1.编写脚本for1.sh&#xff0c;使用for循环创建20账户&#xff0c;账户名前缀由用户从键盘输入&#xff0c;账户初始密码由用户输入&#xff0c;例如&#xff1a;test1、test2、test3、......、test10 首先创建并编写for1.sh 查看结果 2.编写脚本for2.sh&#xff0c;使用…

Java - Lambda 表达式

一、背景 Lambda表达式是Java SE 8中一个重要的新特性。lambda表达式允许你通过表达式来代替功能接口。 lambda表达式就和方法一样,它提供了一个正常的参数列表和一个使用这些参数的主体(body,可以是一个表达式或一个代码块)。 Lambda 表达式&#xff08;Lambda expression&am…

【数据库】— 2NF、3NF、BCNF、最小函数依赖集例题

判断范式级别 设有关系模式W(C,P,S,G,T,R)&#xff0c;其中各属性的含义是&#xff1a;C课程&#xff0c;P教师&#xff0c;S学生&#xff0c;G成绩&#xff0c;T时间&#xff0c;R教室&#xff0c;根据定义有如下数据依赖集 D{ C→P&#xff0c;(S,C)→G&#xff0c;(T,R)→C&…

时序预测 | MATLAB实现SSA-LSTM、LSTM麻雀算法优化长短期记忆神经网络时间序列预测(含优化前后对比)

时序预测 | MATLAB实现SSA-LSTM、LSTM麻雀算法优化长短期记忆神经网络时间序列预测(含优化前后对比) 目录 时序预测 | MATLAB实现SSA-LSTM、LSTM麻雀算法优化长短期记忆神经网络时间序列预测(含优化前后对比)预测效果基本介绍程序设计参考资料 预测效果 基本介绍 麻雀搜索算法(…

Redis最新版本安装教程——Linux操作系统(Centos7)

Redis是基于C语言编写的&#xff0c;因此首先需要安装Redis所需要的gcc依赖&#xff1a; yum install -y gcc tcl 下载安装包并解压 Redis的官方网站地址&#xff1a;RedisRedis is an open source (BSD licensed), in-memory data structure store, used as a database, cach…

【前端下载文件流详解】前端实现多种类型文件(word,excel,pdf,rar,zip等)的下载,接口返回文件流形式(附源码)

【写在前面】其实之前我也写了有关java实现文件的下载&#xff0c;但是当时是局限于excel文档&#xff0c;针对其他类型的并没有介绍&#xff0c;这次刚好有个客户现场反馈回来的&#xff0c;说我们系统确实能下载报告&#xff0c;但是甲方领导要看所有的报告&#xff0c;这不我…

【持续升级】Vue3 系统入门与项目实战无密分享闻郎江上唱歌声

Vue3 系统入门指南&#xff1a;开发高效、可维护的Web应用 【持续升级】Vue3 系统入门与项目实战 下栽のke呈&#xff1a;https://www.666xit.com/3811/ Vue.js是目前最流行的JavaScript框架之一&#xff0c;它的最新版本Vue3已经发布。Vue3具有更好的性能和可维护性&#x…

HNU数据结构与算法分析-小班6

HNU数据结构与算法分析-小班6

一文带你入门MySQL

目录 一、MySQL登陆1.配置MySQL环境变量2.MySQL登陆命令 二、MySQL基础知识1.数据类型&#xff08;1&#xff09;整型&#xff08;2&#xff09;浮点型&#xff08;3&#xff09;日期型&#xff08;4&#xff09;字符型&#xff08;5&#xff09;数据类型小结 2.MySQL的约束【重…

​力扣解法汇总1105. 填充书架

目录链接&#xff1a; 力扣编程题-解法汇总_分享记录-CSDN博客 GitHub同步刷题项目&#xff1a; https://github.com/September26/java-algorithms 原题链接&#xff1a;力扣 描述&#xff1a; 给定一个数组 books &#xff0c;其中 books[i] [thicknessi, heighti] 表示第…

【c语言习题】return中途结束 函数调用

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; 给大家跳段街舞感谢支持&#xff01;ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ…

王道计组(23版)6_总线

总线概述 特点 分时&#xff1a;同一时刻只允许一个部件向总线发送信息 共享&#xff1a;总线上可以挂接多个部件&#xff0c;各个部件互相交换的信息都可以通过这组线路分时共享&#xff0c;多个部件可以同时从总线接收相同的信息 分类 片内总线&#xff1a;CPU芯片内部AL…

如何实现延时任务(订单到期关闭)

目录 一、被动关闭 二、定时任务 三、JDK自带的DelayQueue 四、Netty的时间轮 五、Kafka的时间轮 六、RocketMQ延迟消息 七、RabbitMQ死信队列 八、RabbitMQ插件 九、Redis过期监听 十、Redis的zset 十一、Redisson Redis 总结 在电商、支付等系统中&#xff0c;…

【某软件消息的加解密分析】

首先来熟悉一下ida的使用。 首先找到打开ida&#xff0c;点击"debugger"-> “run” -> “local windows debugger"后&#xff0c;从Application中找到程序并点击"ok”&#xff0c;程序停在入口处。 因为我们要调试的是接收数据包的加解密过程&…

MIT6.S081操作系统实验2021(xv6系统)——lab1 Xv6 and Unix utilities

MIT6.S081操作系统实验2021——lab1 参考文章 sleep 要求为xv6实现UNIX 程序sleep&#xff1b;其应该暂停用户指定的ticks number。tick是 xv6 内核定义的时间概念&#xff0c;即计时器芯片的两次中断之间的时间&#xff08;两次时钟中断之间的时间&#xff09;。您的解决方…