JavaScript设计模式「基于ES2024」:行为型模式-备忘录模式

62 阅读2分钟

备忘录模式是一种行为设计模式,它允许在不破坏封装的情况下捕获和恢复对象的内部状态。这种模式在需要实现撤销/重做功能,或者需要保存对象状态快照的场景中特别有用。

// 备忘录类
class TextEditorMemento {
    #content;
    #cursorPosition;

    constructor(content, cursorPosition) {
        this.#content = content;
        this.#cursorPosition = cursorPosition;
    }

    getContent() {
        return this.#content;
    }

    getCursorPosition() {
        return this.#cursorPosition;
    }
}

// 原发器类
class TextEditor {
    #content = '';
    #cursorPosition = 0;

    type(text) {
        this.#content = this.#content.slice(0, this.#cursorPosition) + text + this.#content.slice(this.#cursorPosition);
        this.#cursorPosition += text.length;
        console.log(`Typed: "${text}". Current content: "${this.#content}"`);
    }

    moveCursor(position) {
        if (position >= 0 && position <= this.#content.length) {
            this.#cursorPosition = position;
            console.log(`Cursor moved to position ${position}`);
        } else {
            console.log("Invalid cursor position");
        }
    }

    delete(length) {
        if (this.#cursorPosition + length <= this.#content.length) {
            this.#content = this.#content.slice(0, this.#cursorPosition) + this.#content.slice(this.#cursorPosition + length);
            console.log(`Deleted ${length} character(s). Current content: "${this.#content}"`);
        } else {
            console.log("Cannot delete: not enough characters after cursor");
        }
    }

    save() {
        return new TextEditorMemento(this.#content, this.#cursorPosition);
    }

    restore(memento) {
        this.#content = memento.getContent();
        this.#cursorPosition = memento.getCursorPosition();
        console.log(`State restored. Current content: "${this.#content}", Cursor at: ${this.#cursorPosition}`);
    }

    getContent() {
        return this.#content;
    }
}

// 负责人类
class History {
    #mementos = [];
    #currentIndex = -1;

    push(memento) {
        this.#mementos.splice(this.#currentIndex + 1);
        this.#mementos.push(memento);
        this.#currentIndex++;
    }

    undo() {
        if (this.#currentIndex > 0) {
            this.#currentIndex--;
            return this.#mementos[this.#currentIndex];
        }
        return null;
    }

    redo() {
        if (this.#currentIndex < this.#mementos.length - 1) {
            this.#currentIndex++;
            return this.#mementos[this.#currentIndex];
        }
        return null;
    }
}

// 客户端代码
function demonstrateMemento() {
    const editor = new TextEditor();
    const history = new History();

    // 初始操作
    editor.type("Hello ");
    history.push(editor.save());

    editor.type("world");
    history.push(editor.save());

    editor.type("!");
    history.push(editor.save());

    // 撤销操作
    console.log("\nUndo operation:");
    let undoMemento = history.undo();
    if (undoMemento) editor.restore(undoMemento);

    console.log("\nUndo operation:");
    undoMemento = history.undo();
    if (undoMemento) editor.restore(undoMemento);

    // 重做操作
    console.log("\nRedo operation:");
    let redoMemento = history.redo();
    if (redoMemento) editor.restore(redoMemento);

    // 新的操作
    console.log("\nNew operation:");
    editor.moveCursor(5);
    editor.type("beautiful ");
    history.push(editor.save());

    console.log("\nFinal content:");
    console.log(editor.getContent());
}

demonstrateMemento();

实现思路

  1. TextEditorMemento 类(备忘录)

    • 存储文本编辑器的状态(内容和光标位置)。
    • 使用私有字段来保护状态,只通过方法访问。
  2. TextEditor 类(原发器)

    • 包含文本编辑的核心功能(输入、删除、移动光标)。
    • 实现 save 方法创建当前状态的备忘录。
    • 实现 restore 方法从备忘录恢复状态。
  3. History 类(负责人)

    • 管理备忘录对象。
    • 实现撤销(undo)和重做(redo)功能。
    • 使用数组和索引来跟踪备忘录历史。

优点

  • 封装性:原发器的内部状态被封装在备忘录中,不暴露给其他对象。
  • 简化原发器:将存储状态的职责转移到备忘录对象,简化了原发器。
  • 定义窄接口和宽接口:备忘录可以对原发器暴露宽接口,对其他对象暴露窄接口。
  • 易于实现撤销/重做:通过管理备忘录的历史,可以轻松实现多步撤销和重做。