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

备忘录模式也叫快照模式,具体来说,就是在不违背封装原则的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便之后恢复对象为先前的状态。这个模式的定义表达了两部分内容:一部分是,存储副本以便后期恢复;

另一部分是,要在不违背封装原则的前提下,进行对象的备份和恢复。备忘录模式的应用场景也比较明确和有限,主要是用来防丢失、撤销、恢复等。它跟平时我们常说的“备份”很相似。两者的主要区别在于,备忘录模式更侧重于代码的设计和实现,备份更侧重架构设计或产品设计。

对于大对象的备份来说,备份占用的存储空间会比较大,备份和恢复的耗时会比较长。针对这个问题,不同的业务场景有不同的处理方式。比如,只备份必要的恢复信息,结合最新的数据来恢复;再比如,全量备份和增量备份相结合,低频全量备份,高频增量备份,两者结合来做恢复。

核心概念

备忘录模式包含三个关键角色:

  1. 发起人(Originator)​​:需要保存和恢复状态的对象

  2. 备忘录(Memento)​​:存储发起人对象内部状态的对象

  3. 管理者(Caretaker)​​:负责保存和管理备忘录的对象

模式结构

classDiagram
    class Originator {
        -state: string
        +CreateMemento(): Memento
        +SetMemento(Memento): void
    }
    
    class Memento {
        -state: string
        +GetState(): string
        +SetState(string): void
    }
    
    class Caretaker {
        -memento: Memento
        +GetMemento(): Memento
        +SetMemento(Memento): void
    }
    
    Originator --> Memento: 创建
    Caretaker o--> Memento: 持有

典型实现

C# 示例代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DesignPatternsPractice.Src.Behavioral.MementoDesignPattern2
{
    /// <summary>
    /// 实现一个备忘录模式  
    ///
    /// </summary>
    public class MementoDesignPattern
    {

        // static void Main()
        // {
        //     InputText currentText = new InputText();
        //     SnapshotHolder snapshotHolder = new SnapshotHolder();

        //     // 收到控制台的 本文
        //     for (; ; )
        //     {
        //         string? readText = Console.ReadLine();
        //         if (string.IsNullOrEmpty(readText))
        //         {
        //             Console.WriteLine("空啊的大哥!!");
        //             return;
        //         }

        //         switch (readText)
        //         {
        //             case ":list":
        //                 Console.WriteLine(currentText.text.ToString());
        //                 break;
        //             case ":undo":
        //                 snapshotHolder.Pop();
        //                 Console.WriteLine(snapshotHolder.GetInputTextsToString());
        //                 break;
        //             default:
        //                 snapshotHolder.Push(snapshotHolder.CreateSnapshot(readText));
        //                 currentText.Append(readText);
        //                 break;
        //         }
        //     }

        // }
    }
    // 输入的文本对象 
    public class InputText
    {
        public StringBuilder text = new StringBuilder();


        public string GetText()
        {
            return text.ToString();
        }

        public void Append(string textappend)
        {
            text.Append(textappend);
        }

        public void restText(Snapshot inputText)
        {
            
        }

    }

    /// <summary>
    /// 保存 InputText 快照
    /// </summary>
    public class SnapshotHolder
    {
        private Stack<Snapshot> inputTexts = new Stack<Snapshot>();

        public Snapshot Pop()
        {
            return inputTexts.Pop();
        }

        public void Push(Snapshot inputText)
        {
            inputTexts.Push(inputText);
        }

        public string GetInputTextsToString()
        {
            string text = "";
            foreach (var item in inputTexts)
            {
                text += item.getText();
            }
            return text;
        }

        public Snapshot CreateSnapshot(string text)
        {
            return new Snapshot(text.ToString());
        }


    }

    // 快照文字对象
    public class Snapshot
    {
        private string _text;

        public Snapshot(string text)
        {
            _text = text;
        }

        public string getText()
        {
            return _text;
        }
    }
}

应用场景

备忘录模式适用于以下情况:

  1. 撤销/重做功能​:如文本编辑器、绘图软件中的撤销操作

  2. 游戏存档​:保存和恢复游戏进度

  3. 事务回滚​:数据库事务处理中的回滚机制

  4. 状态快照​:需要记录对象在某个时刻的状态

优缺点

优点

  1. 封装性好​:不暴露对象实现细节即可保存状态

  2. 简化发起人​:将状态保存和恢复的职责分离

  3. 可维护性​:提供了一种可恢复的机制

  4. 可扩展性​:易于增加新的状态保存点

缺点

  1. 资源消耗​:频繁保存状态可能消耗大量内存

  2. 性能问题​:大对象的状态保存可能影响性能

  3. 实现复杂度​:需要合理设计备忘录的存储方式

实际应用案例

1. 文本编辑器撤销功能

// 文本编辑器备忘录
public class TextEditorMemento
{
    public string Content { get; }
    public DateTime SnapshotTime { get; }
    
    public TextEditorMemento(string content)
    {
        Content = content;
        SnapshotTime = DateTime.Now;
    }
}

// 文本编辑器
public class TextEditor
{
    private string _content = string.Empty;
    private readonly Stack<TextEditorMemento> _history = new Stack<TextEditorMemento>();
    
    public void Type(string text)
    {
        _content += text;
        Save();
    }
    
    public void Save()
    {
        _history.Push(new TextEditorMemento(_content));
    }
    
    public void Undo()
    {
        if (_history.Count > 0)
        {
            var memento = _history.Pop();
            _content = memento.Content;
        }
    }
    
    public string GetContent() => _content;
}

2. 游戏存档系统

// 游戏存档
public class GameSave
{
    public int Level { get; }
    public int Score { get; }
    public DateTime SaveTime { get; }
    
    public GameSave(int level, int score)
    {
        Level = level;
        Score = score;
        SaveTime = DateTime.Now;
    }
}

// 游戏角色
public class GameCharacter
{
    private int _level = 1;
    private int _score = 0;
    
    public void LevelUp()
    {
        _level++;
        _score += 100;
    }
    
    public GameSave CreateSave() => new GameSave(_level, _score);
    
    public void LoadSave(GameSave save)
    {
        _level = save.Level;
        _score = save.Score;
    }
    
    public void DisplayStatus() => 
        Console.WriteLine($"Level: {_level}, Score: {_score}");
}

进阶应用

1. 增量备忘录

只保存状态的变化部分,减少内存使用:

public class IncrementalMemento
{
    public string ChangedState { get; }
    public string ChangeType { get; } // "add", "remove", etc.
    
    public IncrementalMemento(string changedState, string changeType)
    {
        ChangedState = changedState;
        ChangeType = changeType;
    }
}

2. 持久化备忘录

将备忘录保存到数据库或文件中:

public interface IMementoStorage
{
    void SaveMemento(Memento memento);
    Memento LoadMemento();
}

public class DatabaseMementoStorage : IMementoStorage
{
    public void SaveMemento(Memento memento)
    {
        // 保存到数据库
    }
    
    public Memento LoadMemento()
    {
        // 从数据库加载
    }
}

与其他模式的关系

  1. 与命令模式​:备忘录模式常与命令模式一起实现撤销操作

  2. 与原型模式​:都可以用于保存对象状态,但原型模式是通过克隆整个对象

  3. 与状态模式​:备忘录可以保存状态模式中的状态历史

备忘录模式是一种强大的设计模式,特别适合需要状态保存和恢复的场景。正确使用它可以提高系统的灵活性和用户体验。