您现在的位置是:首页 >技术交流 >备忘录设计模式(Memento Pattern)[论点:概念、组成角色、示例代码、框架中的运用、适用场景]网站首页技术交流

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

力不竭!!!战不止!!! 2023-06-08 12:00:02
简介备忘录设计模式(Memento Pattern)[论点:概念、组成角色、示例代码、框架中的运用、适用场景]

概念

备忘录模式(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. 需要在不违反封装原则的前提下,暂存对象的内部状态:备忘录模式允许对象在不暴露其实现细节的情况下,保存和恢复其内部状态。这符合封装原则,有助于保持代码的整洁和易于维护。
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。