备忘录模式

什么是备忘录模式

Java中的备忘录模式(Memento Pattern)是一种行为型设计模式,它允许在不破坏封装性的前提下捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后可以将对象恢复到原先保存的状态。

主要角色包括:

  • 发起者(Originator):需要保存和恢复状态的对象。它记录当前时刻的内部状态信息,提供创建备忘录和恢复备忘录数据的功能,并可以访问备忘录里的所有信息。
  • 备忘录(Memento):负责存储发起人的内部状态。它是一个临时中间对象,用于存储目标对象的初始相关属性信息。当需要恢复对象的状态时,备忘录提供这些内部状态给发起人。
  • 看护者(Caretaker):对备忘录进行管理,提供保存与获取备忘录的功能。但它不能对备忘录的内容进行访问与修改。

优点:

  • 状态保存与恢复:备忘录模式可以方便地保存和恢复对象的状态,使得对象的状态变化具有可追溯性。
  • 封装性:通过将对象的状态封装在备忘录对象中,备忘录模式可以保持对象的封装性,不会暴露内部状态给外部对象。
  • 简化撤销和重做操作:在需要实现撤销、重做等功能的场景中,备忘录模式可以大大简化操作,提高代码的清晰度和可维护性。

缺点:

  • 资源消耗:如果对象的状态较为复杂或状态变化频繁,备忘录模式可能会消耗较多的内存资源来保存状态。
  • 性能开销:频繁地创建和销毁备忘录对象可能会导致一定的性能开销。
  • 设计复杂性:如果对象的状态需要保密或访问权限受限,备忘录模式可能会增加设计的复杂性,并可能破坏对象的封装性。

常见应用场景:

  • 文本编辑器:在文本编辑器中,备忘录模式可以用于实现撤销和重做功能。当用户编辑文本时,编辑器可以定期保存文本状态到备忘录对象中。当用户需要撤销或重做操作时,编辑器可以恢复或重新应用这些状态。
  • 游戏存档:在电子游戏中,备忘录模式可以用于实现游戏的存档功能。当玩家选择保存游戏时,游戏可以将当前的游戏状态(如玩家位置、分数、物品等)保存到备忘录对象中。当玩家再次加载游戏时,游戏可以从备忘录对象中恢复状态,让玩家继续之前的游戏进度。
  • 数据库事务:在数据库操作中,备忘录模式可以用于实现事务的回滚功能。当执行一系列数据库操作时,可以将数据库的状态保存到备忘录对象中。如果事务执行失败或需要回滚,可以恢复到之前的状态。

案例

java实现控制台输入内容的回退

UML

03-JAVA设计模式-备忘录模式-LMLPHP

实现步骤:

  • 创建发起者ScannerInput,定义保存输入内容字段input
  • 创建备忘录继承发起者,主要继承发起者的属性及get/set方法不再重复提供,提供创建备忘录及通过备忘录恢复状态的方法
  • 创建看护者,定义栈对备忘录进行管理,提供保存与获取备忘录的功能。但它不能对备忘录的内容进行访问与修改

实现代码

ScannerInput.java

// 发起者
public class ScannerInput {
    // 输入内容
    protected String input;
    public ScannerInput(String input) {
        this.input = input;
    }
    public String getInput() {
        return input;
    }
    public void setInput(String input) {
        this.input = input;
    }
}

Originator.java

// 备忘录
//  继承发起者,主要继承发起者的属性及get/set方法不再重复提供
//  提供创建备忘录及通过备忘录恢复状态的方法
public class Originator extends ScannerInput{

    public Originator(String input) {
        super(input);
    }

    // 创建备忘录
    public ScannerInput createMemento() {
        return new ScannerInput(this.input);
    }

    // 恢复
    public void restoreMemento(ScannerInput scannerInput) {
        this.input = scannerInput.getInput();
    }
}

Caretaker.java

import java.util.Stack;

// 看护者
// * 对备忘录进行管理,提供保存与获取备忘录的功能。但它不能对备忘录的内容进行访问与修改。
public class Caretaker {
    // 定义栈管理备忘录
    private Stack<ScannerInput> stack = new Stack<ScannerInput>();

    // 压栈
    public void setScannerInput(ScannerInput scannerInput) {
        stack.push(scannerInput);
    }

    // 出栈
    public ScannerInput getScannerInput() {
        if(stack.size() == 0){
            System.out.println("已经不可以回退啦!");
        }
        return stack.pop();
    }
}

TestClient.java

import java.util.Scanner;

public class TestClient {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入一些文本,输入'exit'退出:");

        // 创建原发器对象并设置默认内容
        Originator originator = new Originator("");
        // 创建并保存备忘录
        Caretaker caretaker = new Caretaker();
        // 初始化压栈
        caretaker.setScannerInput(originator.createMemento());

        while (true) {
            String input = scanner.nextLine();
            if ("exit".equalsIgnoreCase(input)) {
                System.out.printf("你输入了:%s%n" , input);
                break;
            }
            // 回退操作
            else if ("back".equalsIgnoreCase(input)) {
                // 回退
                originator.restoreMemento(caretaker.getScannerInput());
                System.out.printf("回退上一步-你输入:%s%n 输入:back(回退上一步) exit(退出)%n" , originator.getInput());
            } else {
                // 设置内容
                originator.setInput(input);
                // 设置备忘录
                caretaker.setScannerInput(originator.createMemento());
                System.out.printf("你输入了:%s%n 输入:back(回退上一步) exit(退出)%n" , originator.getInput());
            }
        }

        scanner.close();
        System.out.println("已退出。");
    }
}

执行结果:

03-JAVA设计模式-备忘录模式-LMLPHP

gitee源码

04-26 06:26