备忘录模式是一种行为设计模式,它允许在不破坏封装性的前提下,捕获并外部化一个对象的内部状态,以便稍后可以将该对象恢复到原先保存的状态。
备忘录模式也叫快照模式,具体来说,就是在不违背封装原则的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便之后恢复对象为先前的状态。这个模式的定义表达了两部分内容:一部分是,存储副本以便后期恢复;
另一部分是,要在不违背封装原则的前提下,进行对象的备份和恢复。备忘录模式的应用场景也比较明确和有限,主要是用来防丢失、撤销、恢复等。它跟平时我们常说的“备份”很相似。两者的主要区别在于,备忘录模式更侧重于代码的设计和实现,备份更侧重架构设计或产品设计。
对于大对象的备份来说,备份占用的存储空间会比较大,备份和恢复的耗时会比较长。针对这个问题,不同的业务场景有不同的处理方式。比如,只备份必要的恢复信息,结合最新的数据来恢复;再比如,全量备份和增量备份相结合,低频全量备份,高频增量备份,两者结合来做恢复。
核心概念
备忘录模式包含三个关键角色:
发起人(Originator):需要保存和恢复状态的对象
备忘录(Memento):存储发起人对象内部状态的对象
管理者(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. 文本编辑器撤销功能
// 文本编辑器备忘录
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()
{
// 从数据库加载
}
}
与其他模式的关系
与命令模式:备忘录模式常与命令模式一起实现撤销操作
与原型模式:都可以用于保存对象状态,但原型模式是通过克隆整个对象
与状态模式:备忘录可以保存状态模式中的状态历史
备忘录模式是一种强大的设计模式,特别适合需要状态保存和恢复的场景。正确使用它可以提高系统的灵活性和用户体验。