Java设计模式之备忘录模式

概念和作用

备忘录模式是一种行为设计模式,它让你能够保存一个对象的内部状态,以便在适当的时候恢复对象的先前状态。其主要作用是提供了一种可以恢复对象内部状态而不破坏封装性的机制。

使用场景

备忘录模式适用于需要保存一个对象的状态快照,以便在将来恢复到这个状态的场景。例如,在文本编辑器中实现撤回(undo)功能,游戏中的存档与读档功能,以及在需要事务管理的系统中用于回滚操作等。

示例

1.发起人(即被备份的)类

复制代码
public class Originator {
    private String text;

    public Originator(String text) {
        this.text = text;
    }

    // 创建备忘录,保存当前状态
    public Memento createMemento() {
        return new Memento(text);
    }

    // 恢复备忘录,恢复到之前的状态
    public void restoreMemento(Memento memento) {
        this.text = memento.getText();
    }

    // 获取和设置文本内容
    public String getText() {
        return text;
    }

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

2.备忘录类

复制代码
public class Memento {
    private final String text;

    public Memento(String text) {
        this.text = text;
    }

    // 获取文本内容
    public String getText() {
        return text;
    }
}

3.管理者类

复制代码
import java.util.ArrayList;
import java.util.List;

public class Caretaker {
    private List<Memento> mementos = new ArrayList<>();

    // 保存备忘录
    public void addMemento(Memento memento) {
        mementos.add(memento);
    }

    // 获取指定索引的备忘录
    public Memento getMemento(int index) {
        if (index >= 0 && index < mementos.size()) {
            return mementos.get(index);
        }
        return null;
    }

    // 获取备忘录数量
    public int getMementoCount() {
        return mementos.size();
    }

    // 清除所有备忘录
    public void clearMementos() {
        mementos.clear();
    }
}

4.客户端代码示例

复制代码
public class Client {
    public static void main(String[] args) {
        Originator originator = new Originator("初始文本内容");
        Caretaker caretaker = new Caretaker();

        // 保存当前状态到备忘录
        caretaker.addMemento(originator.createMemento());
        System.out.println("保存状态到备忘录: " + originator.getText());

        // 修改文本内容
        originator.setText("新的文本内容");
        System.out.println("修改后的文本内容: " + originator.getText());

        // 再次保存状态到备忘录
        caretaker.addMemento(originator.createMemento());
        System.out.println("保存状态到备忘录: " + originator.getText());

        // 修改文本内容
        originator.setText("再次修改的文本内容");
        System.out.println("修改后的文本内容: " + originator.getText());

        // 恢复到上一个状态
        if (caretaker.getMementoCount() > 0) {
            originator.restoreMemento(caretaker.getMemento(caretaker.getMementoCount() - 1));
            System.out.println("恢复到上一个状态: " + originator.getText());
        }

        // 恢复到初始状态
        if (caretaker.getMementoCount() > 1) {
            originator.restoreMemento(caretaker.getMemento(0));
            System.out.println("恢复到初始状态: " + originator.getText());
        }
    }
}

优缺点

优点

1.封装性:它将发起人对象的状态封装在备忘录对象中,外界无法访问备忘录对象中的状态信息,从而保证了发起人对象的封装性。

2.简单易用:备忘录模式提供了一种简单且有效的保存和恢复对象状态的机制,易于理解和使用。

3.灵活的恢复机制:通过备忘录管理器,可以灵活地保存多个状态,并根据需要恢复到任意一个之前的状态。

缺点

1.资源消耗:如果对象的状态信息很多,那么保存每个状态的备忘录对象会占用较多的内存资源,可能导致性能问题。

2.管理复杂性:随着保存的备忘录数量增加,管理和维护这些备忘录可能会变得复杂,需要考虑如何有效地存储和检索备忘录。

不使用备忘录模式的实现

如果不使用备忘录模式,可以通过在发起人对象中直接维护一个历史记录列表来实现类似的功能。例如:

复制代码
import java.util.ArrayList;
import java.util.List;

public class OriginatorWithoutMemento {
    private String text;
    private List<String> history = new ArrayList<>();

    public OriginatorWithoutMemento(String text) {
        this.text = text;
    }

    // 保存当前状态到历史记录
    public void saveState() {
        history.add(text);
    }

    // 恢复到上一个状态
    public void restorePreviousState() {
        if (!history.isEmpty()) {
            text = history.remove(history.size() - 1);
        }
    }

    // 获取和设置文本内容
    public String getText() {
        return text;
    }

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

在客户端代码中,可以这样使用:

复制代码
public class ClientWithoutMemento {
    public static void main(String[] args) {
        OriginatorWithoutMemento originator = new OriginatorWithoutMemento("初始文本内容");

        // 保存当前状态
        originator.saveState();
        System.out.println("保存状态到历史记录: " + originator.getText());

        // 修改文本内容
        originator.setText("新的文本内容");
        System.out.println("修改后的文本内容: " + originator.getText());

        // 再次保存状态
        originator.saveState();
        System.out.println("保存状态到历史记录: " + originator.getText());

        // 修改文本内容
        originator.setText("再次修改的文本内容");
        System.out.println("修改后的文本内容: " + originator.getText());

        // 恢复到上一个状态
        originator.restorePreviousState();
        System.out.println("恢复到上一个状态: " + originator.getText());

        // 恢复到初始状态
        originator.restorePreviousState();
        System.out.println("恢复到初始状态: " + originator.getText());
    }
}

这种方式虽然也能实现类似的功能,但它将状态保存和恢复的逻辑与发起人对象紧密耦合在一起,违反了单一职责原则。

而且,如果需要更复杂的管理功能(如限制保存的历史记录数量、根据特定条件恢复等),会使发起人对象的代码变得更加复杂。

相比之下,备忘录模式通过将状态保存和恢复的职责分离到独立的类中,提供了更清晰、灵活和可扩展的解决方案。

相关推荐
T_Wizard6 分钟前
后端转 AI 应用开发,我先把这 12 个名词理清了:一张图看懂架构、RAG、Tool Calling 与 MCP
java·ai编程
杨航 AI20 分钟前
JEV的用法
java·前端·数据库
灿飞33 分钟前
Kotlin与Java虚拟机七:空安全:把十亿美元的错误消灭在类型系统里
java·kotlin
知识搬运临时工42 分钟前
JVM 删掉偏向锁之后,一次无竞争锁从 0.3 纳秒变成 5.4 纳秒
java
IT枫斗者枫哥1 小时前
用了BigDecimal还差一分?用四组Java代码查清原因
java·后端
一条泥憨鱼1 小时前
【消息队列】ACK确认了什么:消息队列可靠投递的完整链路
java·后端·kafka·消息队列·rabbitmq
从琳开始9892 小时前
网络原理入门(二)------OSI七层模型?TCP/IP五层(四层)模型原来是这样的
java·网络·tcp/ip·java-ee·php
文丑颜不良啊2 小时前
LeetCode1861~1880题目解析
java·leetcode
Flynn Liang2 小时前
AgentScope Java 深度解析:ReActAgent、HarnessAgent 与多智能体编排——JVM 上的自治 Agent 底座
java·大模型·架构设计·ai agent·spring ai·langchain4j·agentscope
Flynn Liang2 小时前
上线后怎么排查 AI 调用?Spring AI Observability:token/延迟/成本埋点与全链路追踪
java·人工智能·spring·监控·可观测性·observability·springai