# 备忘录模式

# 概念

备忘录模式是一种行为设计模式,它允许保存一个对象的内部状态,以便在需要时恢复对象的先前状态。其核心作用是提供一种在不破坏封装性的前提下恢复对象内部状态的机制。

# 作用

1.封装性保障:将对象状态封装在备忘录中,外部无法直接访问状态细节,确保发起人对象的封装性。

2.状态管理简化:提供简单有效的状态保存与恢复机制,降低状态管理复杂度。

3.灵活恢复支持:通过备忘录管理者(Caretaker)可保存多个状态快照,支持恢复到任意历史状态。

# 场景

1.需要保存对象状态快照的场景:如文本编辑器的撤销(undo)功能、游戏存档与读档功能。

2.事务回滚需求:在需要事务管理的系统中,用于操作失败时的状态回滚。

3.历史状态追踪:需要记录对象状态变化过程,以便审计或调试的场景。

# 举例

以下是备忘录模式的Java代码示例,包含发起人类、备忘录类、管理者类和客户端代码:

// 发起人类(被备份对象)
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;
    }
}
// 备忘录类(存储状态)
public class Memento {
    private final String text;

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

    // 获取文本内容
    public String getText() {
        return text;
    }
}
// 管理者类(管理备忘录)
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();
    }
}
// 客户端代码示例
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());
        }
    }
}

# 反例

不使用备忘录模式时,可能通过在发起人对象中直接维护历史记录列表实现类似功能,但存在明显缺点:

// 不使用备忘录的发起人类
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());
    }
}

此反例的缺点在于:

1.状态保存与恢复逻辑与发起人对象紧密耦合,违反单一职责原则。

2.若需扩展复杂管理功能(如限制历史记录数量、条件恢复等),发起人类代码会变得臃肿,降低可维护性。

# 原理

备忘录模式通过将对象状态的保存与恢复职责分离到独立的备忘录类和管理者类中,实现状态管理的解耦。发起人对象仅负责创建和恢复备忘录,备忘录类封装状态细节,管理者类负责备忘录的存储与检索,从而在保证封装性的同时提供灵活的状态管理机制。

# 缺点

1.资源消耗问题:若对象状态复杂或保存频率高,备忘录对象可能占用大量内存,影响性能。

2.管理复杂度增加:随着备忘录数量增长,需设计有效的存储与检索策略,否则可能降低系统效率。

总结

备忘录模式通过独立的备忘录和管理者类,实现了对象状态的安全保存与恢复,适用于需要状态快照和回滚的场景。其优点在于保持封装性、简化状态管理,但需注意资源消耗和管理复杂度。相比直接在发起人中维护历史记录的反例,备忘录模式提供了更清晰、灵活的解决方案。



微信公众号

QQ交流群
原创网站开发,偏差难以避免。

如若发现错误,诚心感谢反馈。

愿你倾心相念,愿你学有所成。

愿你朝华相顾,愿你前程似锦。