什么是备忘录模式?

备忘录模式(Memento Pattern)是一种行为型设计模式,它用于捕获一个对象的内部状态,并在不暴露对象实现细节的前提下,将状态保存起来,以便后续需要时恢复该对象到之前的状态。

其核心思想是:通过一个 “备忘录” 对象存储目标对象的状态,目标对象(原发器)负责创建和恢复备忘录,而 “负责人” 对象负责管理备忘录的存储和获取,从而实现状态的快照保存与回滚。

备忘录模式的核心角色

  1. 原发器(Originator):需要保存状态的对象,能创建备忘录(存储当前状态)和从备忘录恢复状态。
  2. 备忘录(Memento):存储原发器的内部状态,对外部(尤其是负责人)隐藏状态的具体结构,只允许原发器访问。
  3. 负责人(Caretaker):负责管理备忘录,提供保存(添加备忘录)和获取(检索备忘录)的接口,但不操作备忘录的内容。

JavaScript 实现备忘录模式示例:文本编辑器撤销功能

以下以 “文本编辑器” 为例,实现备忘录模式。场景是:用户在编辑器中输入文本时,系统会定期保存文本状态(快照),当用户点击 “撤销” 时,可恢复到上一次保存的状态。

代码实现
// -------------- 1. 备忘录(Memento):存储文本状态 --------------
class TextMemento {
  /**
   * 构造函数:存储原发器的状态
   * @param {string} content - 文本内容
   */
  constructor(content) {
    this._content = content; // 私有属性(约定用下划线表示),仅允许原发器访问
  }

  /**
   * 供原发器获取状态的方法(对外隐藏实现)
   */
  getContent() {
    return this._content;
  }
}

// -------------- 2. 原发器(Originator):文本编辑器 --------------
class TextEditor {
  constructor() {
    this.content = ""; // 当前文本内容
  }

  /**
   * 输入文本(修改状态)
   * @param {string} text - 新增的文本
   */
  type(text) {
    this.content += text;
  }

  /**
   * 创建备忘录(保存当前状态)
   * @returns {TextMemento} 包含当前状态的备忘录
   */
  save() {
    return new TextMemento(this.content);
  }

  /**
   * 从备忘录恢复状态
   * @param {TextMemento} memento - 备忘录对象
   */
  restore(memento) {
    this.content = memento.getContent();
  }
}

// -------------- 3. 负责人(Caretaker):管理备忘录(撤销历史) --------------
class HistoryManager {
  constructor() {
    this.mementos = []; // 存储备忘录的数组(撤销历史)
  }

  /**
   * 保存备忘录(添加到历史)
   * @param {TextMemento} memento - 备忘录对象
   */
  push(memento) {
    this.mementos.push(memento);
  }

  /**
   * 获取上一个备忘录(用于撤销)
   * @returns {TextMemento|null} 上一个状态的备忘录
   */
  pop() {
    return this.mementos.pop() || null;
  }
}

// -------------- 4. 客户端使用 --------------
// 创建编辑器(原发器)和历史管理器(负责人)
const editor = new TextEditor();
const history = new HistoryManager();

// 操作1:输入文本并保存状态
editor.type("Hello, ");
history.push(editor.save()); // 保存状态1:"Hello, "
console.log("当前内容:", editor.content); // 输出:当前内容: Hello, 

// 操作2:继续输入并保存
editor.type("World!");
history.push(editor.save()); // 保存状态2:"Hello, World!"
console.log("当前内容:", editor.content); // 输出:当前内容: Hello, World!

// 操作3:撤销(恢复到上一个状态)
const lastMemento = history.pop();
if (lastMemento) {
  editor.restore(lastMemento);
  console.log("撤销后内容:", editor.content); // 输出:撤销后内容: Hello, World!(此时历史中还剩状态1)
}

// 操作4:再次撤销(恢复到最初状态)
const firstMemento = history.pop();
if (firstMemento) {
  editor.restore(firstMemento);
  console.log("再次撤销后内容:", editor.content); // 输出:再次撤销后内容: Hello, 
}

代码说明

  1. 备忘录(TextMemento)

    • 存储文本编辑器的核心状态(_content),通过 getContent 方法向原发器暴露状态,对负责人隐藏内部实现(如不允许负责人直接修改 _content)。
  2. 原发器(TextEditor)

    • type 方法修改文本内容(状态变化)。
    • save 方法创建备忘录,将当前 content 存入备忘录。
    • restore 方法从备忘录中读取状态,恢复自身 content
  3. 负责人(HistoryManager)

    • 用数组 mementos 存储备忘录,提供 push(保存)和 pop(获取最近状态)方法。
    • 不处理备忘录的内容,仅负责管理存储,避免直接访问原发器的内部状态。
  4. 核心优势

    • 状态封装:备忘录只允许原发器访问状态,保护了原发器的封装性(外部无法直接修改状态)。
    • 可恢复性:通过保存的备忘录,可随时将对象恢复到历史状态(如编辑器的撤销功能)。
    • 简化原发器:原发器无需自己管理历史状态,委托给负责人,符合 “单一职责原则”。

备忘录模式的常见应用场景

  • 撤销 / 重做功能:如文本编辑器、图像编辑软件(PS 的历史记录)、表单填写(回退到上一步)。
  • 快照备份:如数据库的事务回滚(保存事务执行前的状态)、游戏存档(保存当前游戏进度)。
  • 状态日志:需要记录对象状态变化历史,以便追踪或审计的场景。

注意事项

  • 内存开销:频繁保存备忘录可能占用大量内存(如长时间编辑的文档),可通过限制备忘录数量(如只保留最近 10 步)或压缩状态优化。
  • 深拷贝需求:若原发器的状态包含引用类型(如对象、数组),备忘录需深拷贝状态,避免原发器修改后影响已保存的备忘录。

备忘录模式的核心是 “状态快照与安全恢复”,通过分离状态的创建、存储和恢复逻辑,实现了对象状态的灵活管理,是实现撤销、存档等功能的经典方案。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐